home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / printing / scriptable print simpletext / simpletext.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  120.5 KB  |  4,542 lines

  1. /*
  2. **    File:        SimpleText.c
  3. **
  4. **    Contains:    SimpleText - a simple document editing application for shipping
  5. **                             with system software.
  6. **
  7. **    Version:    SimpleText 1.4 or later
  8. **
  9. ** Copyright 1993-1999 Apple Computer. All rights reserved.
  10. **
  11. **    You may incorporate this sample code into your applications without
  12. **    restriction, though the sample code has been provided "AS IS" and the
  13. **    responsibility for its operation is 100% yours.  However, what you are
  14. **    not permitted to do is to redistribute the source as "DSC Sample Code"
  15. **    after having made changes. If you're going to re-distribute the source,
  16. **    we require that you make it clear in the source that the code was
  17. **    descended from Apple Sample Code, but that you've made changes.
  18.  
  19. */
  20.  
  21. #include "MacIncludes.h"
  22.  
  23. #include <ImageCompression.h>    // for CustomGetFilePreview
  24. #include <Threads.h>
  25.  
  26. #define CompilingMain=1
  27. #include "SimpleText.h"
  28. #include "Clipboard.h"
  29. #include "ExtendPrintRecord.h"
  30. #include "ScriptablePrinting.h"
  31.  
  32. // --------------------------------------------------------------------------------------------------------------
  33. // FORWARD DECLARES
  34. // --------------------------------------------------------------------------------------------------------------
  35. OSErr     DoActivate(WindowRef pWindow, Boolean activating);
  36. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult);
  37. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls);
  38. Boolean CommandToIDs(short commandID, short * menuID, short *itemID);
  39. void     AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn);
  40.  
  41. // --------------------------------------------------------------------------------------------------------------
  42. // GLOBAL VARIABLES
  43. // --------------------------------------------------------------------------------------------------------------
  44. EventRecord            gEvent;                    // currently pending event
  45. Boolean                gAllDone;                // true if the application is the in process of terminating
  46. MachineInfoRec        gMachineInfo;            // info about abilities and options installed on this machine
  47. short                gApplicationResFile;    // resource fork of application
  48. RgnHandle            gCursorRgn;                // region to control the cursor apearence
  49. AGRefNum            gAGRefNum = -1;            // AppleGuide database which is open
  50. FSSpec                gAGSpec;                // where to find our database
  51. AGCoachRefNum        gAGCoachRefNum = -1;    // coach handler refNum
  52. FontMappingHandle    gFontMappingList = nil;    // list of font mappings
  53. ThreadID            gFontThread;            // thread that builds font menu
  54. ThreadID            gAGThread;                // thread that looks for AppleGuide database
  55. ThreadID            gStarterThread;            // starts our other threads for us
  56. Boolean                gDontYield;                // whether our threads should yield
  57. void*                gThreadResults;            // scratch space for thread results
  58.  
  59. // These variables are for the find/replace commands
  60. Str255            gFindString = "\p", gReplaceString = "\p";
  61. Boolean            gWrapAround = false, gCaseSensitive = false;
  62.  
  63. // Metrowerks MWCRuntime.lib defines qd for us on PPC, and their
  64. // __runtime module does under the 68K case. OTOH, neither SC nor
  65. // MrC give us qd for free, so we need it there. I'm still not
  66. // certain which way to go for the ThinkC or Symantec PPC case.
  67. #if !defined(__MWERKS__)
  68. // QuickDraw globals
  69. QDGlobals        qd;
  70. #endif
  71.  
  72. // --------------------------------------------------------------------------------------------------------------
  73. #pragma segment Utility
  74.  
  75. static pascal Boolean AlertFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  76. {
  77.     if (theEvent->what == activateEvt && (DialogRef) theEvent->message == theDialog)
  78.         {
  79.         SetDialogDefaultItem(theDialog, 1);
  80.         }
  81.  
  82.     if (StdFilterProc(theDialog, theEvent, itemHit))
  83.         return true;
  84.  
  85.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  86.     // drastically changing how the system handles the menu bar during our alert)
  87.     if (theEvent->what == updateEvt /* || theEvent->what == activateEvt */ )
  88.         {
  89.         HandleEvent(theEvent);
  90.         }
  91.  
  92.     return false;
  93.  
  94. } // AlertFilter
  95.  
  96.  
  97. void ConductErrorDialog(OSErr error, short commandID, short alertType)
  98. {
  99.     long        foundError;            // The error, converted to a number
  100.     short        stringIndex;        // Index into the strings
  101.     Str255        errorText;            // the error in a string format
  102.     
  103.     // Start with no error so far
  104.     foundError = 0;
  105.     
  106.     // Start with the first string
  107.     stringIndex = 1;
  108.     
  109.     // Loop until we find an error string
  110.     errorText[0] = 0;
  111.  
  112.     do
  113.         {
  114.         // Get the string, and convert it to a number
  115.         GetIndString(errorText, kErrorBaseID + commandID, stringIndex);
  116.         if (errorText[0] == 0)
  117.             break;
  118.         StringToNum(errorText, &foundError);
  119.         
  120.         // If we reach the last string, or we match the error code
  121.         if ((foundError == 0) ||
  122.             (foundError == error))
  123.             {
  124.             // Get the text string for this error
  125.             GetIndString(errorText, kErrorBaseID + commandID, stringIndex+1);
  126.             }
  127.         else
  128.             {
  129.             // Otherwise, make us continue until we get a string
  130.             errorText[0] = 0;
  131.             }
  132.             
  133.         // Advance so we get the next string number
  134.         stringIndex += 2;
  135.         
  136.         } while (errorText[0] == 0);                // errorText[0] == 0
  137.         
  138.     if (errorText[0] != 0)
  139.         {
  140.         DialogRef    dPtr;
  141.         short        hit;
  142.         
  143.         SetCursor(&qd.arrow);
  144.         ParamText(errorText, "\p", "\p", "\p");
  145.         
  146.         #if !GENERATINGPOWERPC
  147.             if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  148.                 {
  149.                 short ** hDialog;
  150.                 
  151.                 hDialog = (short**) GetResource('DLOG', kErrorBaseID + alertType);
  152.                 (*hDialog)[4] = dBoxProc;
  153.                 
  154.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  155.                 
  156.                 do
  157.                     {
  158.                     ModalDialog(nil, &hit);
  159.                     } while (hit != ok);
  160.                 
  161.                 DisposeDialog(dPtr);
  162.                 }
  163.             else
  164.                 {
  165.                 dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  166.                 
  167.                 SetDialogDefaultItem(dPtr, ok);
  168.                 
  169.                 BeginMovableModal();
  170.                 
  171.                 do
  172.                     {
  173.                     MovableModalDialog(nil, &hit);
  174.                     } while (hit != ok);
  175.                 
  176.                 DisposeDialog(dPtr);
  177.                 EndMovableModal();
  178.                 }
  179.         #else
  180.             dPtr = GetNewDialog(kErrorBaseID + alertType, nil, (WindowRef)-1);
  181.             
  182.             SetDialogDefaultItem(dPtr, ok);
  183.             
  184.             BeginMovableModal();
  185.             
  186.             do
  187.                 {
  188.                 MovableModalDialog(nil, &hit);
  189.                 } while (hit != ok);
  190.             
  191.             DisposeDialog(dPtr);
  192.             EndMovableModal();
  193.         #endif
  194.         }
  195.         
  196. } // ConductErrorDialog
  197.  
  198. // --------------------------------------------------------------------------------------------------------------
  199. #pragma segment Utility
  200.  
  201. static void MovableModalMenus(DialogRef dPtr, short *pItem, long menuResult)
  202. {
  203.     short    iCut, iCopy, iClear, iPaste;
  204.     short    editMenu;
  205.     short    menuItem = menuResult & 0xFFFF;
  206.     
  207.     // find out where edit menus are
  208.     CommandToIDs(cCut, &editMenu, &iCut);
  209.     CommandToIDs(cCopy, &editMenu, &iCopy);
  210.     CommandToIDs(cClear, &editMenu, &iClear);
  211.     CommandToIDs(cPaste, &editMenu, &iPaste);
  212.     
  213.     HiliteMenu(0);
  214.     switch (menuResult >> 16)
  215.         {
  216.         case mApple:
  217.             {
  218.             Str255    tempString;
  219.             
  220.             GetMenuItemText(GetMenuHandle(menuResult>>16), menuItem, tempString);
  221.             OpenDeskAcc(tempString);
  222.             }
  223.             break;
  224.             
  225.         case mEdit:
  226.             {
  227.             short    type;
  228.             Handle    item;
  229.             Rect    box;
  230.             short    editField = GetDialogKeyboardFocusItem(dPtr);
  231.             
  232.             // return typed item, if it isn't disabled
  233.             GetDialogItem(dPtr, editField, &type, &item, &box);
  234.             if ((type & itemDisable) == 0)
  235.                 *pItem = editField;
  236.                 
  237.             if (menuItem == iCut)
  238.                 {
  239.                 DialogCut(dPtr);
  240.                 ZeroScrap();
  241.                 TEToScrap();
  242.                 }
  243.                 
  244.             if (menuItem == iCopy)
  245.                 {
  246.                 DialogCopy(dPtr);
  247.                 ZeroScrap();
  248.                 TEToScrap();
  249.                 }
  250.                 
  251.             if (menuItem == iClear)
  252.                 DialogDelete(dPtr);
  253.                 
  254.             if (menuItem == iPaste)
  255.                 DialogPaste(dPtr);
  256.             }
  257.             break;
  258.         }
  259.         
  260. } // MovableModalMenus
  261.  
  262. // --------------------------------------------------------------------------------------------------------------
  263. #pragma segment Utility
  264.  
  265. void MovableModalDialog(ModalFilterProcPtr filterProc, short *pItem)
  266. /*
  267.     Call this as you would ModalDialog, when the dialog is moveable
  268.     modal.
  269.     
  270.     However, first call BeginMovableModal, and afterwards (after
  271.     disposing of dialog) call EndMovableModal.
  272. */
  273. {
  274.     GrafPtr     curPort;
  275.     DialogRef    dPtr = FrontWindow();
  276.     
  277.     *pItem = 0;    
  278.     if (dPtr)
  279.         {
  280.         GetPort(&curPort);
  281.         SetPort(dPtr);
  282.         
  283.         do
  284.             {
  285.             WaitNextEvent(mDownMask + mUpMask + keyDownMask + keyUpMask + autoKeyMask + updateMask + activMask + osMask,
  286.                             &gEvent, 0, nil);
  287.             
  288.             // call the filter proc
  289.             if ( (filterProc) && ((*filterProc) (dPtr, &gEvent, pItem)) )
  290.                 break;
  291.                             
  292.             // call the basic filtering
  293.             if (StdFilterProc(dPtr, &gEvent, pItem))
  294.                 break;
  295.                 
  296.             // handle keyboard
  297.             if ((gEvent.what == keyDown) && (gEvent.modifiers & cmdKey))
  298.                 {
  299.                 MovableModalMenus(dPtr, pItem, MenuKey(gEvent.message & charCodeMask));
  300.                 break;
  301.                 }
  302.                 
  303.             // handle clicks and drags
  304.             if (gEvent.what == mouseDown)
  305.                 {
  306.                 WindowRef    whichWindow;
  307.                 short        part = FindWindow(gEvent.where, &whichWindow);
  308.                 
  309.                 // menu bar events
  310.                 if (part == inMenuBar)
  311.                     {
  312.                     MovableModalMenus(dPtr, pItem, MenuSelect(gEvent.where));
  313.                     break;
  314.                     }
  315.                     
  316.                 // check for outside of our window
  317.                 if (!PtInRgn(gEvent.where, ((WindowPeek)dPtr)->strucRgn))
  318.                     {
  319.                     SysBeep(1);
  320.                     gEvent.what = nullEvent;
  321.                     }
  322.                     
  323.                 // drag the window around
  324.                 if ( (part == inDrag) && (whichWindow == dPtr) )
  325.                     {
  326.                     Rect    tempRect = (**GetGrayRgn()).rgnBBox;
  327.                     
  328.                     DragWindow(GetDialogWindow(dPtr), gEvent.where, &tempRect);
  329.                     gEvent.what = nullEvent;
  330.                     }
  331.                 }
  332.                 
  333.             // check with standard dialog stuff    
  334.             {
  335.             DialogRef    tempDialog;
  336.             
  337.             if ( IsDialogEvent(&gEvent) && DialogSelect(&gEvent, &tempDialog, pItem) )
  338.                 break;
  339.             }
  340.             
  341.             // handle updates
  342.             if (gEvent.what == updateEvt)
  343.                 {
  344.                 HandleEvent(&gEvent);
  345.                 break;
  346.                 }
  347.             } while (true);
  348.         
  349.         SetPort(curPort);
  350.         }
  351.         
  352. } // MovableModalDialog
  353.  
  354. // --------------------------------------------------------------------------------------------------------------
  355. #pragma segment Utility
  356.  
  357. void BeginMovableModal(void)
  358. {
  359.     DialogRef    dPtr = FrontWindow();
  360.     WindowRef    nextWindow = GetNextWindow(dPtr);
  361.     
  362.     if (nextWindow)
  363.         DoActivate(nextWindow, false);
  364.     AdjustMenus(GetDialogWindow(dPtr), (GetDialogKeyboardFocusItem(dPtr) > 0), false);
  365.  
  366. } // BeginMovableModal
  367.  
  368. // --------------------------------------------------------------------------------------------------------------
  369. #pragma segment Utility
  370.  
  371. void EndMovableModal(void)
  372. {
  373.     WindowRef    nextWindow = FrontWindow();
  374.     
  375.     AdjustMenus(nextWindow, true, false);
  376.     if (nextWindow)
  377.         DoActivate(nextWindow, true);
  378.     
  379. } // EndMovableModal
  380.  
  381. // --------------------------------------------------------------------------------------------------------------
  382. #pragma segment Utility
  383.  
  384. short ConductFindOrReplaceDialog(short dialogID)
  385. {
  386.     DialogRef    dPtr;
  387.     short        hit;
  388.     
  389.     dPtr = GetNewDialog(dialogID, nil, (WindowRef)-1);
  390.     if (dPtr)
  391.         {
  392.         short    kind;
  393.         Rect    box;
  394.         Handle    item;
  395.         
  396.         // standard default behavior
  397.         SetDialogDefaultItem(dPtr, ok);
  398.         SetDialogCancelItem (dPtr, cancel);
  399.         SetDialogTracksCursor(dPtr, true);
  400.         
  401.         // Find string
  402.         GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  403.         SetDialogItemText(item, gFindString);
  404.  
  405.         // check boxes
  406.         GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  407.         SetControlValue((ControlRef)item, gCaseSensitive);
  408.         GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  409.         SetControlValue((ControlRef)item, gWrapAround);
  410.         
  411.         if (dialogID == kReplaceWindowID)
  412.             {
  413.             // Replace string
  414.             GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  415.             SetDialogItemText(item, gReplaceString);
  416.             }
  417.         
  418.         // select the search text by default
  419.         SelectDialogItemText(dPtr, iFindEdit, 0, 32767);
  420.         
  421.         // and away we go!
  422.         ShowWindow(GetDialogWindow(dPtr));
  423.         BeginMovableModal();
  424.         
  425.         do
  426.             {
  427.             MovableModalDialog(nil, &hit);
  428.             switch (hit)
  429.                 {
  430.                 case iCaseSensitive:
  431.                 case iWrapAround:
  432.                     GetDialogItem(dPtr, hit, &kind, &item, &box);
  433.                     SetControlValue((ControlRef)item, 1-GetControlValue((ControlRef)item));
  434.                     break;
  435.                 }
  436.             } while ( (hit != ok) && (hit != cancel) && (hit != iReplaceAll) );
  437.         
  438.         if (hit != cancel)
  439.             {
  440.             // Find string
  441.             GetDialogItem(dPtr, iFindEdit, &kind, &item, &box);
  442.             GetDialogItemText(item, gFindString);
  443.     
  444.             // check boxes
  445.             GetDialogItem(dPtr, iCaseSensitive, &kind, &item, &box);
  446.             gCaseSensitive = GetControlValue((ControlRef)item);
  447.             GetDialogItem(dPtr, iWrapAround, &kind, &item, &box);
  448.             gWrapAround = GetControlValue((ControlRef)item);
  449.             
  450.             if (dialogID == kReplaceWindowID)
  451.                 {
  452.                 // Replace string
  453.                 GetDialogItem(dPtr, iReplaceEdit, &kind, &item, &box);
  454.                 GetDialogItemText(item, gReplaceString);
  455.                 }
  456.             }
  457.             
  458.         DisposeDialog(dPtr);
  459.         EndMovableModal();
  460.         }
  461.         
  462.     return(hit);
  463.     
  464. } // ConductFindOrReplaceDialog
  465.  
  466. // --------------------------------------------------------------------------------------------------------------
  467. #pragma segment Utility
  468.  
  469. void SetWatchCursor(void)
  470. {
  471.     CursHandle    theWatch;
  472.         
  473.     theWatch = GetCursor(watchCursor);
  474.     if (theWatch)
  475.         {
  476.         char    oldState;
  477.         
  478.         oldState = HGetState((Handle) theWatch);
  479.         HLock((Handle) theWatch);
  480.         SetCursor(*theWatch);
  481.         HSetState((Handle) theWatch, oldState);
  482.         }
  483.         
  484. } // SetWatchCursor
  485.  
  486. // --------------------------------------------------------------------------------------------------------------
  487. #pragma segment Utility
  488.  
  489. void LongRectToRect(LongRect* longRect, Rect *rect)
  490. {
  491.     rect->top         = longRect->top;
  492.     rect->left         = longRect->left;
  493.     rect->bottom     = longRect->bottom;
  494.     rect->right     = longRect->right;
  495.     
  496. } // LongRectToRect
  497.  
  498. // --------------------------------------------------------------------------------------------------------------
  499. #pragma segment Utility
  500.  
  501. void RectToLongRect(Rect *rect, LongRect *longRect)
  502. {
  503.     longRect->top         = rect->top;
  504.     longRect->left         = rect->left;
  505.     longRect->bottom     = rect->bottom;
  506.     longRect->right     = rect->right;
  507.     
  508. } // RectToLongRect
  509.  
  510. // --------------------------------------------------------------------------------------------------------------
  511. #pragma segment Utility
  512.  
  513. #ifndef ff
  514. #define ff(x) (x << 16)
  515. #endif
  516.  
  517. void GetPICTRectangleAt72dpi(PicHandle hPicture, Rect *pictureRect)
  518. {
  519.     typedef struct FixedRect {
  520.         Fixed left;
  521.         Fixed top;
  522.         Fixed right;
  523.         Fixed bottom;
  524.     } FixedRect;
  525.     
  526.     typedef struct {
  527.         Picture                pictInfo;
  528.         unsigned short        versionOp;        // 0x1101
  529.         Byte                opCodes[1];
  530.     } PICTHeaderVer1;
  531.     
  532.     typedef struct {
  533.         Picture            pictInfo;
  534.         unsigned short    versionOp;        // 0x0011
  535.         unsigned short    versionOp2;        // 0x02ff
  536.         unsigned short    headerOp;        // 0x0c00
  537.         unsigned short    version;        // 0xffff
  538.         unsigned short    version2;        // 0xffff
  539.         FixedRect        pictBounds;
  540.         unsigned long    reserved;
  541.         unsigned short    opCodes[1];
  542.     } PICTHeaderVer2;
  543.     
  544.     typedef struct {
  545.         Picture            pictInfo;
  546.         unsigned short    versionOp;        // 0x0011
  547.         unsigned short    versionOp2;        // 0x02ff
  548.         unsigned short    headerOp;        // 0x0c00
  549.         unsigned short    version;        // 0xfffe
  550.         unsigned short    reserved;        // 0x0000
  551.         Fixed            hRes;
  552.         Fixed            vRes;
  553.         Rect            pictBounds;
  554.         unsigned long    reserved2;
  555.         unsigned short    opCodes[1];
  556.     } PICTHeaderVer2Ext;
  557.     
  558.     const Fixed seventyTwo = 0x480000;
  559.  
  560.     Fixed            hRes, vRes;
  561.     PICTHeaderVer1* pPict = (PICTHeaderVer1*) *hPicture;
  562.  
  563.     hRes = vRes = seventyTwo;        // assume 72 dpi, Fixed
  564.  
  565.     if (pPict->versionOp == 0x0011) 
  566.         {    
  567.         // Version 2 PICT
  568.     
  569.         PICTHeaderVer2* pPict2 = (PICTHeaderVer2*) pPict;
  570.         
  571.         if (pPict2->version == 0xfffe) 
  572.             {    
  573.             // Extended Version 2
  574.             PICTHeaderVer2Ext* pPict2ext = (PICTHeaderVer2Ext*) pPict;
  575.             hRes = pPict2ext->hRes;
  576.             vRes = pPict2ext->vRes;
  577.             }
  578.         }
  579.  
  580.     hRes = FixDiv(hRes, seventyTwo);
  581.     vRes = FixDiv(vRes, seventyTwo);
  582.     pictureRect->left     = Fix2Long(FixDiv( ff((**hPicture).picFrame.left),         hRes ));
  583.     pictureRect->right     = Fix2Long(FixDiv( ff((**hPicture).picFrame.right),     hRes ));
  584.     pictureRect->top     = Fix2Long(FixDiv( ff((**hPicture).picFrame.top),         vRes ));
  585.     pictureRect->bottom = Fix2Long(FixDiv( ff((**hPicture).picFrame.bottom),     vRes ));
  586.     
  587. } // GetPICTRectangleAt72dpi
  588.  
  589. #undef ff
  590. // --------------------------------------------------------------------------------------------------------------
  591. #pragma segment Utility
  592.  
  593. static WindowDataPtr    GetWindowInfo(WindowRef pWindow)
  594. {
  595.     WindowDataPtr result = nil;
  596.     
  597.     if     (
  598.         (pWindow) &&
  599.         (GetWindowKind(pWindow) == userKind)
  600.         )
  601.         result = (WindowDataPtr) GetWRefCon(pWindow);
  602.  
  603.     return result;
  604.     
  605. } // GetWindowInfo
  606.  
  607. // --------------------------------------------------------------------------------------------------------------
  608. #pragma segment Utility
  609.  
  610. static short ZeroStringSub(Str255 destString, Str255 subStr)
  611.     // returns number of substitutions performed
  612. {
  613.     OSErr    anErr;
  614.     Handle    destHandle = nil;
  615.     Handle    subHandle = nil;
  616.     short    count = 0;
  617.  
  618.     anErr = PtrToHand(&destString[1], &destHandle, destString[0]);
  619.     if (anErr == noErr)
  620.         {        
  621.         anErr = PtrToHand(&subStr[1], &subHandle, subStr[0]);
  622.         if (anErr == noErr)
  623.             {
  624.             count = ReplaceText(destHandle, subHandle, "\p^0");        // error or # of substitutions
  625.                         
  626.             destString[0] = GetHandleSize(destHandle);
  627.             BlockMoveData(*destHandle, &destString[1], destString[0]);
  628.             }
  629.         }
  630.  
  631.     DisposeHandle(destHandle);
  632.     DisposeHandle(subHandle);
  633.  
  634.     if (count < 0)
  635.         count = 0;        // change error code into count = 0 substitutions
  636.  
  637.     return count;
  638.  
  639. } // ZeroStringSub
  640.  
  641. // --------------------------------------------------------------------------------------------------------------
  642. // SEARCH/REPLACE UTILITY FUNCTIONS
  643. // --------------------------------------------------------------------------------------------------------------
  644. static Boolean IsThisTheString(
  645.             Ptr p,                        // pointer to check
  646.             Str255 searchString,        // string to check for
  647.             Boolean isCaseSensitive)    // case sensitive check or not
  648. /*
  649.     Returns true if the supplied string is at the specified offset.
  650.     Otherwise returns false.
  651. */
  652. {
  653.     Boolean    returnValue = false;
  654.     
  655.     if (isCaseSensitive)
  656.         returnValue = ( IUMagString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  657.     else
  658.         returnValue = ( IUMagIDString(p, &searchString[1], searchString[0], searchString[0]) == 0 );
  659.         
  660.     return(returnValue);
  661.     
  662. } // IsThisTheString
  663.  
  664. // --------------------------------------------------------------------------------------------------------------
  665.  
  666. Boolean PerformSearch(
  667.         Handle    h,                    // handle to search
  668.         long start,                    // offset to begin with
  669.         Str255 searchString,        // string to search for
  670.         Boolean isCaseSensitive,    // case sensitive search
  671.         Boolean isBackwards,        // search backwards from starting point
  672.         Boolean isWraparound,        // wrap search around from end->begining
  673.         long * pNewStart,            // returned new selection start
  674.         long * pNewEnd)                // returned new selection end
  675. /*
  676.     Performs a search on the supplied handle, starting at the provided
  677.     offset.  Returns the new selection start and end values, and true
  678.     if the search is successful.  Otherwise it returns false.
  679. */
  680. {
  681.     char    flags;
  682.     Ptr        startPtr;
  683.     Ptr        endPtr;
  684.     Ptr        searchPtr;
  685.     Boolean    foundIt = false;
  686.     
  687.     flags = HGetState(h);
  688.     HLock(h);
  689.             
  690.     // back up one when searching backwards, or we'll hit every time on the current
  691.     // character
  692.     if (isBackwards)
  693.         {
  694.         if (start != 0)
  695.             {
  696.             --start;
  697.             }
  698.         else
  699.             {
  700.             if (isWraparound)
  701.                 start = GetHandleSize(h);
  702.             else
  703.                 return(false);
  704.             }
  705.         }
  706.         
  707.     // determine the bounds of the searching
  708.     startPtr = (*h) + start;
  709.     if ( isWraparound )
  710.         {
  711.         if (isBackwards)
  712.             {
  713.             // go backwards until just after the start, or begining of
  714.             // document is start is the end
  715.             if (start == GetHandleSize(h))
  716.                 endPtr = *h;
  717.             else
  718.                 endPtr = startPtr + 1;
  719.             }
  720.         else
  721.             {
  722.             // go forwards until just before the start, or to the end
  723.             // of the document is the start is already the begining
  724.             if (start == 0)
  725.                 endPtr = *h + GetHandleSize(h);
  726.             else
  727.                 endPtr = startPtr - 1;
  728.             }
  729.         }
  730.     else
  731.         {
  732.         if (isBackwards)
  733.             {
  734.             // go back until hit begining of document
  735.             endPtr = *h-1;    
  736.             }
  737.         else
  738.             {
  739.             // go forward until hit end of document
  740.             endPtr = *h + GetHandleSize(h);
  741.             }
  742.         }
  743.         
  744.     searchPtr = startPtr;
  745.     while (searchPtr != endPtr)
  746.         {
  747.         if (IsThisTheString(searchPtr, searchString, isCaseSensitive))
  748.             {
  749.             foundIt = true;
  750.             *pNewStart = searchPtr - *h;
  751.             *pNewEnd = *pNewStart + searchString[0];
  752.             break;
  753.             }
  754.             
  755.         if (isBackwards)
  756.             --searchPtr;
  757.         else
  758.             ++searchPtr;
  759.             
  760.         if (isWraparound)
  761.             {
  762.             if (searchPtr < *h)
  763.                 searchPtr = *h + GetHandleSize(h);
  764.             if (searchPtr > *h + GetHandleSize(h))
  765.                 searchPtr = *h;
  766.             }
  767.         }
  768.         
  769.     HSetState(h, flags);
  770.     
  771.     return(foundIt);
  772.     
  773. } // PerformSearch
  774.  
  775. // --------------------------------------------------------------------------------------------------------------
  776. // SELECTION UTILITY ROUTINES
  777. // --------------------------------------------------------------------------------------------------------------
  778. void DrawSelection(WindowDataPtr pData, Rect *pSelection, short * pPhase, Boolean bumpPhase)
  779. {
  780.     if    (!EmptyRect(pSelection) ) 
  781.         {
  782.         RgnHandle    oldClip = NewRgn();
  783.         Pattern        aPattern;
  784.         Rect        newClip;
  785.  
  786.         
  787.         if     ( 
  788.             (bumpPhase) && 
  789.             (MOVESELECTION(TickCount()) ) 
  790.             )
  791.             {
  792.             if ((++(*pPhase)) > 7 )
  793.                 *pPhase = 1;
  794.             }
  795.             
  796.         // setup for drawing in this window
  797.         SetPort((GrafPtr) pData);
  798.         GetClip(oldClip);
  799.         PenMode(notPatXor);
  800.         
  801.         // offset the draw area (SetOrigin a must to preserve pattern appearence)
  802.         // and the clip area to avoid stepping on the scroll bars
  803.         SetOrigin(GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  804.         newClip = pData->contentRect;
  805.         OffsetRect(&newClip, GetControlValue(pData->hScroll), GetControlValue(pData->vScroll));
  806.         ClipRect(&newClip);
  807.         
  808.         // do the draw
  809.         GetIndPattern(&aPattern, kPatternListID, (*pPhase)+1);
  810.         PenPat(&aPattern);
  811.         FrameRect(pSelection);
  812.         SetOrigin(0, 0);
  813.         
  814.         // restore the old port settings
  815.         SetClip(oldClip);
  816.         DisposeRgn(oldClip);
  817.         PenNormal();
  818.  
  819.         }
  820.  
  821. } // DrawSelection
  822.  
  823. // --------------------------------------------------------------------------------------------------------------
  824. OSErr SelectContents(WindowRef pWindow, WindowDataPtr pData, EventRecord *pEvent, Rect *pSelection, Rect *pContent, short *pPhase)
  825. {
  826.  
  827.     OSErr            anErr = noErr;
  828.     Point            clickPoint = pEvent->where;
  829.     Point            currentPoint;
  830.     Boolean         didJustScroll;
  831.     ControlRef        theControl;
  832.     
  833.     GlobalToLocal(&clickPoint);
  834.     if (FindControl(clickPoint, pWindow, &theControl) == 0)
  835.         {
  836.     
  837.         // move the click point into the proper range
  838.         clickPoint.h += GetControlValue(pData->hScroll);
  839.         clickPoint.v += GetControlValue(pData->vScroll);
  840.         
  841.         // if the shift key is held down then the selection starts from
  842.         // a preexisting point such that we are doing an expand/contract
  843.         // of the original selection
  844.         if (pEvent->modifiers & shiftKey)
  845.             {
  846.             if (clickPoint.h < pSelection->right)
  847.                 clickPoint.h = pSelection->right;
  848.             else
  849.                 clickPoint.h = pSelection->left;
  850.  
  851.             if (clickPoint.v < pSelection->bottom)
  852.                 clickPoint.v = pSelection->bottom;
  853.             else
  854.                 clickPoint.v = pSelection->top;
  855.             }
  856.                         
  857.         while (StillDown())
  858.             {                    
  859.             // get the current mouse 
  860.             GetMouse(¤tPoint);
  861.             
  862.             didJustScroll = false;
  863.             // scroll contents if needed
  864.             {
  865.             short    deltaH = 0;
  866.             short    deltaV = 0;
  867.             
  868.             if (currentPoint.h < 0)
  869.                 deltaH = pData->hScrollAmount;
  870.             if (currentPoint.h > qd.thePort->portRect.right)
  871.                 deltaH = -pData->hScrollAmount;
  872.             if (currentPoint.v < 0)
  873.                 deltaV = pData->vScrollAmount;
  874.             if (currentPoint.v > qd.thePort->portRect.bottom)
  875.                 deltaV = -pData->vScrollAmount;
  876.                 
  877.             if ( (deltaH != 0) || (deltaV != 0) )
  878.                 {                
  879.                 if (deltaH)
  880.                     SetControlAndClipAmount(pData->hScroll, &deltaH);
  881.                 if (deltaV)
  882.                     SetControlAndClipAmount(pData->vScroll, &deltaV);
  883.  
  884.                 DoScrollContent(pWindow, pData, deltaH, deltaV);
  885.                 
  886.                 didJustScroll = true;
  887.                 }
  888.             }
  889.             
  890.             // map mouse into proper range
  891.             currentPoint.h += GetControlValue(pData->hScroll);
  892.             currentPoint.v += GetControlValue(pData->vScroll);
  893.     
  894.             // clip to the document size
  895.             if (currentPoint.h < 0)
  896.                 currentPoint.h = 0;
  897.             if (currentPoint.v < 0)
  898.                 currentPoint.v = 0;
  899.             if (currentPoint.h > pContent->right)
  900.                 currentPoint.h = pContent->right;
  901.             if (currentPoint.v > pContent->bottom)
  902.                 currentPoint.v = pContent->bottom;
  903.                 
  904.             // draw the new selection if it is time or we are about to 
  905.             // exit this loop
  906.             if ((MOVESELECTION(TickCount())) || (!Button()) || (didJustScroll) )
  907.                 {
  908.                 // first, erase any old selection we might have had
  909.                 DrawSelection(pData, pSelection, pPhase, false);
  910.  
  911.                 // make a rectangle out of the two points
  912.                 pSelection->left     = Min(currentPoint.h, clickPoint.h);
  913.                 pSelection->right     = Max(currentPoint.h, clickPoint.h);
  914.                 pSelection->top     = Min(currentPoint.v, clickPoint.v);
  915.                 pSelection->bottom     = Max(currentPoint.v, clickPoint.v);
  916.     
  917.                 // draw the new selection
  918.                 DrawSelection(pData, pSelection, pPhase, true);
  919.                 }
  920.             }
  921.         
  922.         // we handled the selection
  923.         anErr = eActionAlreadyHandled;
  924.         }
  925.         
  926.     return(anErr);
  927.     
  928. } // SelectContents
  929.  
  930. // --------------------------------------------------------------------------------------------------------------
  931. void DragAndDropArea(WindowRef pWindow, WindowDataPtr pData, EventRecord* event, Rect *pFrameRect)
  932. {
  933.     RgnHandle        hilightRgn;
  934.     Rect            r;
  935.     DragReference    theDrag;
  936.     OSErr            anErr = noErr;
  937.     
  938.     if (NewDrag(&theDrag) == noErr)
  939.         {
  940.         if (pData->pDragAddFlavors)
  941.             anErr = (*(pData->pDragAddFlavors)) (pWindow, pData, theDrag);
  942.         
  943.         if (anErr == noErr)
  944.             {
  945.             Rect    globalRect = *pFrameRect;
  946.             
  947.             hilightRgn = NewRgn();    
  948.             LocalToGlobal(&TopLeft(globalRect));
  949.             LocalToGlobal(&BotRight(globalRect));
  950.             RectRgn(hilightRgn, &globalRect);
  951.             SetDragItemBounds(theDrag, 1, &r);
  952.     
  953.             // turn the region from a fill into a frame
  954.             {    
  955.                 RgnHandle tempRgn = NewRgn();
  956.     
  957.                 CopyRgn(hilightRgn, tempRgn);
  958.                 InsetRgn(tempRgn, 1, 1);
  959.                 DiffRgn(hilightRgn, tempRgn, hilightRgn);
  960.                 DisposeRgn(tempRgn);
  961.             }
  962.             
  963.             TrackDrag(theDrag, event, hilightRgn);
  964.             DisposeDrag(theDrag);
  965.             DisposeRgn(hilightRgn);
  966.             }
  967.         }
  968.  
  969. } // DragAndDropArea
  970.  
  971. // --------------------------------------------------------------------------------------------------------------
  972. // WINDOW UTILITY ROUTINES
  973. // --------------------------------------------------------------------------------------------------------------
  974. #pragma segment Main
  975.  
  976. static void CalculateGrowIcon(WindowDataPtr pData, Rect * location)
  977. {
  978.     if (pData->vScroll)
  979.         location->top = (**pData->vScroll).contrlRect.bottom;
  980.     else
  981.         {
  982.         if (pData->hScroll)
  983.             location->top = (**pData->hScroll).contrlRect.top;
  984.         else
  985.             location->top = pData->theWindow.port.portRect.bottom - 15;
  986.         }
  987.         
  988.     if (pData->hScroll)
  989.         location->left = (**pData->hScroll).contrlRect.right;
  990.     else
  991.         {
  992.         if (pData->vScroll)
  993.             location->left = (**pData->vScroll).contrlRect.left;
  994.         else
  995.             location->left = pData->theWindow.port.portRect.right - 15;
  996.         }
  997.         
  998.     location->right = location->left + 16;
  999.     location->bottom = location->top + 16;
  1000.     
  1001. } // CalculateGrowIcon
  1002.  
  1003. // --------------------------------------------------------------------------------------------------------------
  1004. #pragma segment Main
  1005.  
  1006. OSErr    AdjustScrollBars(WindowRef pWindow,
  1007.     Boolean moveControls,                 // might the controls have moved?
  1008.     Boolean didResize,                     // did we just resize the window?
  1009.     Boolean *needInvalidate)            // does the caller need to invalidate contents as a result?
  1010. {
  1011.     OSErr            anErr = noErr;
  1012.     LongRect        docRect;
  1013.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1014.     Rect            growIconRect;
  1015.     
  1016.     if (needInvalidate)
  1017.         *needInvalidate = false;
  1018.  
  1019.     if (pData)
  1020.         {
  1021.         short    oldHMax, oldVMax;
  1022.         short    oldHValue, oldVValue;
  1023.         
  1024.         // cache current values, we'll force an update if we needed to change em!
  1025.         if (pData->hScroll)
  1026.             {
  1027.             oldHMax = GetControlMaximum(pData->hScroll);
  1028.             oldHValue = GetControlValue(pData->hScroll);
  1029.             }
  1030.         if (pData->vScroll)
  1031.             {
  1032.             oldVMax = GetControlMaximum(pData->vScroll);
  1033.             oldVValue = GetControlValue(pData->vScroll);
  1034.             }
  1035.             
  1036.         // if we have a grow box but not all controls we have to invalidate the grow bar areas
  1037.         // by caclulating them
  1038.         if ( (didResize) && (pData->hasGrow) )
  1039.             {
  1040.             // if we regrow without any scroll bars, we need to update the content area
  1041.             if ( (needInvalidate) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  1042.                 *needInvalidate = true;
  1043.             
  1044.             // invalidate old grow bar areas
  1045.             if (pData->vScroll == nil)
  1046.                 {
  1047.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1048.                 growIconRect.left = pData->contentRect.right;
  1049.                 InvalRect(&growIconRect);
  1050.                 }
  1051.             if (pData->hScroll == nil)
  1052.                 {
  1053.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1054.                 growIconRect.top = pData->contentRect.bottom;
  1055.                 InvalRect(&growIconRect);
  1056.                 }
  1057.             
  1058.             // invalidate new grow bar areas
  1059.             if (pData->vScroll == nil)
  1060.                 {
  1061.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1062.                 growIconRect.left = growIconRect.right - kScrollBarSize;
  1063.                 InvalRect(&growIconRect);
  1064.                 }
  1065.             if (pData->hScroll == nil)
  1066.                 {
  1067.                 growIconRect = GetWindowPort(pWindow)->portRect;
  1068.                 growIconRect.top = growIconRect.bottom - kScrollBarSize;
  1069.                 InvalRect(&growIconRect);
  1070.                 }
  1071.             }
  1072.             
  1073.         // if the controls need moving, recalculate the visible area
  1074.         if (moveControls)
  1075.             {
  1076.             pData->contentRect = GetWindowPort(pWindow)->portRect;
  1077.             if ((pData->hScroll) || (pData->hasGrow) )
  1078.                 pData->contentRect.bottom -= kScrollBarSize;
  1079.             if ((pData->vScroll) || (pData->hasGrow) )
  1080.                 pData->contentRect.right -= kScrollBarSize;
  1081.             }
  1082.             
  1083.         // before doing anything, make the controls invisible
  1084.         if (pData->hScroll)
  1085.             (**pData->hScroll).contrlVis = 0;    
  1086.         if (pData->vScroll)
  1087.             (**pData->vScroll).contrlVis = 0;
  1088.  
  1089.         // based on document and visiable area, adjust possible control values
  1090.         if ( (pData->pGetDocumentRect) && ((pData->hScroll) || (pData->vScroll)) )
  1091.             {
  1092.             // let the object calc the size and content if it wishes to
  1093.             anErr = (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, false);
  1094.             if (anErr == noErr)
  1095.                 {
  1096.                 short    amountOver;
  1097.                 short    newMax;
  1098.                 
  1099.                 amountOver = (docRect.right - docRect.left) - (pData->contentRect.right - pData->contentRect.left);
  1100.                 if     (
  1101.                     (pData->hScroll) &&
  1102.                     (amountOver > 0)
  1103.                     )
  1104.                     newMax = amountOver;
  1105.                 else
  1106.                     newMax = 0;
  1107.     
  1108.                 if (pData->hScroll)
  1109.                     {
  1110.                     if (GetControlValue(pData->hScroll) > newMax)
  1111.                         {
  1112.                         if (needInvalidate)
  1113.                             *needInvalidate = true;
  1114.                         }
  1115.                     SetControlMaximum(pData->hScroll, newMax);
  1116.                     }
  1117.                 
  1118.                 amountOver = (docRect.bottom - docRect.top) - (pData->contentRect.bottom - pData->contentRect.top);
  1119.                 if     (
  1120.                     (pData->vScroll) &&
  1121.                     (amountOver > 0)
  1122.                     )
  1123.                     newMax = amountOver;
  1124.                 else
  1125.                     newMax = 0;
  1126.                     
  1127.                 if (pData->vScroll)
  1128.                     {
  1129.                     if (GetControlValue(pData->vScroll) > newMax)
  1130.                         {
  1131.                         if (needInvalidate)
  1132.                             *needInvalidate = true;
  1133.                         }
  1134.                     SetControlMaximum(pData->vScroll, newMax);
  1135.                     }
  1136.                 }
  1137.             }
  1138.             
  1139.         // then, if the controls need moving, we move them and inval the old
  1140.         // and new locations
  1141.         if (moveControls)
  1142.             {
  1143.             // if we have grow box we invalidate the old grow location
  1144.             if ( pData->hasGrow) 
  1145.                 {
  1146.                 CalculateGrowIcon(pData, &growIconRect);
  1147.                 InvalRect(&growIconRect);
  1148.                 }
  1149.                 
  1150.             if (pData->hScroll)
  1151.                 {
  1152.                 short    widthAdjust;
  1153.                 
  1154.                 if ((pData->vScroll) || (pData->hasGrow))
  1155.                     widthAdjust = -kGrowScrollAdjust;
  1156.                 else
  1157.                     widthAdjust = -1;
  1158.                     
  1159.                 growIconRect = (**pData->hScroll).contrlRect;
  1160.                 InvalRect(&growIconRect);
  1161.                 
  1162.                 MoveControl(pData->hScroll, pData->hScrollOffset-1, GetWindowPort(pWindow)->portRect.bottom - kScrollBarSize);
  1163.                 SizeControl(pData->hScroll, (GetWindowPort(pWindow)->portRect.right - 
  1164.                             GetWindowPort(pWindow)->portRect.left) + widthAdjust - pData->hScrollOffset,
  1165.                             16);
  1166.  
  1167.                 growIconRect = (**pData->hScroll).contrlRect;
  1168.                 InvalRect(&growIconRect);
  1169.                 }
  1170.  
  1171.             if (pData->vScroll)
  1172.                 {
  1173.                 short    heightAdjust;
  1174.                 
  1175.                 if ((pData->hScroll) || (pData->hasGrow))
  1176.                     heightAdjust = -kGrowScrollAdjust;
  1177.                 else
  1178.                     heightAdjust = -1;
  1179.                     
  1180.                 growIconRect = (**pData->vScroll).contrlRect;
  1181.                 InvalRect(&growIconRect);
  1182.  
  1183.                 MoveControl(pData->vScroll, GetWindowPort(pWindow)->portRect.right - kScrollBarSize, pData->vScrollOffset-1);
  1184.                 SizeControl(pData->vScroll, 16,
  1185.                             (GetWindowPort(pWindow)->portRect.bottom - 
  1186.                             GetWindowPort(pWindow)->portRect.top) + heightAdjust - pData->vScrollOffset);
  1187.                 growIconRect = (**pData->vScroll).contrlRect;
  1188.                 InvalRect(&growIconRect);
  1189.                 }
  1190.                 
  1191.             // if we have scroll bars, update the grow icon
  1192.             if ( pData->hasGrow )
  1193.                 {
  1194.                 CalculateGrowIcon(pData, &growIconRect);
  1195.                 InvalRect(&growIconRect);
  1196.                 }
  1197.             
  1198.             }
  1199.  
  1200.         // let the document adjust anything it needs to
  1201.         if (pData->pAdjustSize)
  1202.             anErr = (*(pData->pAdjustSize)) (pWindow, pData, &didResize);
  1203.             
  1204.         if ((didResize) && (needInvalidate))
  1205.             *needInvalidate = true;
  1206.  
  1207.  
  1208.         if ( ((WindowPeek) pWindow)->hilited )
  1209.             {
  1210.             // after doing something, make the controls visible
  1211.             if (pData->hScroll)
  1212.                 {
  1213.                 if ((oldHMax != GetControlMaximum(pData->hScroll)) || (oldHValue != GetControlValue(pData->hScroll)) )
  1214.                     ShowControl(pData->hScroll);
  1215.                 else
  1216.                     (**pData->hScroll).contrlVis = 0xFF;    
  1217.                 }
  1218.             if (pData->vScroll)
  1219.                 {
  1220.                 if ((oldVMax != GetControlMaximum(pData->vScroll)) || (oldVValue != GetControlValue(pData->vScroll)) )
  1221.                     ShowControl(pData->vScroll);
  1222.                 else
  1223.                     (**pData->vScroll).contrlVis = 0xFF;
  1224.                 }
  1225.             }
  1226.  
  1227.         }
  1228.         
  1229.     return anErr;
  1230.     
  1231. } // AdjustScrollBars
  1232.  
  1233. // --------------------------------------------------------------------------------------------------------------
  1234. // MENU UTILITY ROUTINES
  1235. // --------------------------------------------------------------------------------------------------------------
  1236. #pragma segment Main
  1237.  
  1238. Boolean CommandToIDs(short commandID, short * menuID, short *itemID)
  1239. {
  1240.  
  1241.     short    ** commandHandle;
  1242.     short    whichMenu;
  1243.     short    oldResFile = CurResFile();
  1244.     Boolean    returnValue = false;
  1245.     
  1246.     UseResFile(gApplicationResFile);
  1247.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1248.         {
  1249.         commandHandle = (short**) Get1Resource('MCMD', whichMenu);
  1250.         if (commandHandle)
  1251.             {
  1252.             short    * pCommands = *commandHandle;
  1253.             short    commandIndex;
  1254.             short    numCommands = pCommands[0];
  1255.             
  1256.             for (commandIndex = 1; commandIndex <= numCommands; ++commandIndex)
  1257.                 if (pCommands[commandIndex] == commandID)
  1258.                     {
  1259.                     *menuID = whichMenu;
  1260.                     *itemID = commandIndex;
  1261.                     
  1262.                     returnValue = (commandIndex == numCommands);
  1263.                     }
  1264.             }    
  1265.         }
  1266.         
  1267.     UseResFile(oldResFile);
  1268.     
  1269.     return returnValue;
  1270.     
  1271. } // CommandToIDs
  1272.  
  1273. // --------------------------------------------------------------------------------------------------------------
  1274. #pragma segment Main
  1275.  
  1276. Boolean IsCommandEnabled(short commandID)
  1277. /*
  1278.     returns true if a given command is currently enabled
  1279. */
  1280. {
  1281.     short        whichMenu, whichItem;
  1282.     MenuHandle    menu;
  1283.     
  1284.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1285.     menu = GetMenuHandle(whichMenu);
  1286.     
  1287.     if ((**menu).enableFlags & (1 << whichItem))
  1288.         return(true);
  1289.     
  1290.     return(false);
  1291.     
  1292. } // IsCommandEnabled
  1293.  
  1294. // --------------------------------------------------------------------------------------------------------------
  1295. #pragma segment Main
  1296.  
  1297. void EnableCommand(short commandID)
  1298. /*
  1299.     Given a command ID, enables the first menu item with that command ID.
  1300.     
  1301.     If the command table for a given menu is less than the number of items in the menu,
  1302.     and the command being enabled is the last item in the command table, then all
  1303.     items from there on down are also enabled.  This is useful for menus that get
  1304.     appended to, such as the desk accessory list, font list, or speaking voices list.
  1305. */
  1306. {
  1307.     short    whichMenu;
  1308.     short    whichItem;
  1309.     
  1310.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1311.         {
  1312.         short        i;
  1313.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1314.         
  1315.         if (menu)
  1316.             {
  1317.             short        numItems = CountMItems(menu);
  1318.             
  1319.             for (i = whichItem; i <= numItems; ++i)
  1320.                 EnableItem(menu, i);
  1321.             }
  1322.         }
  1323.     else
  1324.         {
  1325.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1326.  
  1327.         if (menu)
  1328.             EnableItem(menu, whichItem);
  1329.         }
  1330.         
  1331. } // EnableCommand
  1332.  
  1333. // --------------------------------------------------------------------------------------------------------------
  1334. #pragma segment Main
  1335.  
  1336. void ChangeCommandName(short commandID, short resourceID, short resourceIndex)
  1337. {
  1338.     short        whichMenu;
  1339.     short        whichItem;
  1340.     MenuHandle    menu;
  1341.     
  1342.     // figure out how this command maps into the menu bar
  1343.     CommandToIDs(commandID, &whichMenu, &whichItem);
  1344.     menu = GetMenuHandle(whichMenu);
  1345.     
  1346.     // then make this item into the requested new string
  1347.     {
  1348.     Str255        theString;
  1349.     
  1350.     GetIndString(theString, resourceID, resourceIndex);
  1351.     SetMenuItemText(menu, whichItem, theString);
  1352.     }
  1353.     
  1354. } // ChangeCommandName
  1355.  
  1356. // --------------------------------------------------------------------------------------------------------------
  1357. #pragma segment Main
  1358.  
  1359. void EnableCommandCheck(short commandID, Boolean check)
  1360. {
  1361.  
  1362.     short    whichMenu;
  1363.     short    whichItem;
  1364.     
  1365.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1366.         {
  1367.         short        i;
  1368.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1369.         short        numItems = CountMItems(menu);
  1370.         
  1371.         for (i = whichItem; i <= numItems; ++i)
  1372.             {
  1373.             EnableItem(menu, i);
  1374.             CheckItem(menu, i, check);
  1375.             }
  1376.         }
  1377.     else
  1378.         {
  1379.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1380.  
  1381.         EnableItem(menu, whichItem);
  1382.         CheckItem(menu, whichItem, check);
  1383.         }
  1384.         
  1385. } // EnableCommandCheck
  1386.  
  1387.  
  1388. // --------------------------------------------------------------------------------------------------------------
  1389. #pragma segment Main
  1390.  
  1391. void EnableCommandCheckStyle(short commandID, Boolean check, short style)
  1392. {
  1393.  
  1394.     short    whichMenu;
  1395.     short    whichItem;
  1396.     
  1397.     if (CommandToIDs(commandID, &whichMenu, &whichItem))
  1398.         {
  1399.         short        i;
  1400.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1401.         short        numItems = CountMItems(menu);
  1402.         
  1403.         for (i = whichItem; i <= numItems; ++i)
  1404.             {
  1405.             EnableItem(menu, i);
  1406.             CheckItem(menu, i, check);
  1407.             SetItemStyle(menu, i, style);
  1408.             }
  1409.         }
  1410.     else
  1411.         {
  1412.         MenuHandle    menu = GetMenuHandle(whichMenu);
  1413.  
  1414.         EnableItem(menu, whichItem);
  1415.         CheckItem(menu, whichItem, check);
  1416.         SetItemStyle(menu, whichItem, style);
  1417.         }
  1418.         
  1419. } // EnableCommandCheckStyle
  1420.  
  1421. // --------------------------------------------------------------------------------------------------------------
  1422. #pragma segment Main
  1423.  
  1424. void AdjustMenus(WindowRef pWindow, Boolean editDialogs, Boolean forceTitlesOn)
  1425. {
  1426.     Boolean                 wasEnabled[mNumberMenus];    // Old state of menus
  1427.     short                    whichMenu;                    // for stepping through menus
  1428.     MenuHandle                menu;                        // for reading in menu IDs
  1429.     WindowDataPtr             pData = GetWindowInfo(pWindow);
  1430.     
  1431.     // Step through all of the menus 
  1432.     for (whichMenu = mApple; whichMenu <= mLastMenu; whichMenu++)
  1433.         {
  1434.         // Save the old state of the menu title 
  1435.         menu = GetMenuHandle(whichMenu);
  1436.         if (menu)                                // because contents menu may not be around
  1437.             {
  1438.             if (forceTitlesOn)                
  1439.                 wasEnabled[mLastMenu - whichMenu] = false;
  1440.             else
  1441.                 wasEnabled[mLastMenu - whichMenu] = (((**menu).enableFlags && 1) == 1);
  1442.             
  1443.             // Disable the entire menu 
  1444.             (**menu).enableFlags = 0;        
  1445.             }
  1446.         }
  1447.     
  1448.     // select all, unless someone else changes it
  1449.     ChangeCommandName(cSelectAll, kMiscStrings, iSelectAllCommand);
  1450.  
  1451.     // if we have NO windows, or the current window is one we understand
  1452.     if ((pWindow == nil) || (pData))
  1453.         {
  1454.         // enable the default commands
  1455.         EnableCommand(cAbout);
  1456.         EnableCommand(cDeskAccessory);
  1457.         
  1458.         EnableCommand(cNew);
  1459.         EnableCommand(cOpen);
  1460.         EnableCommand(cQuit);
  1461.     
  1462.         EnableCommand(cShowClipboard);
  1463.         }
  1464.     else
  1465.         {
  1466.         // it's printing or a dialog, so enable cut/copy/paste
  1467.         if (editDialogs)
  1468.             {
  1469.             EnableCommand(cCut);
  1470.             EnableCommand(cCopy);
  1471.             EnableCommand(cPaste);
  1472.             EnableCommand(cClear);
  1473.             }
  1474.         
  1475.         // and desk accs too!        
  1476.         EnableCommand(cDeskAccessory);
  1477.  
  1478.         }
  1479.         
  1480.     if ( (pWindow) && (pData) )
  1481.         {
  1482.         // all windows can be closed
  1483.         if (FrontWindow())
  1484.             EnableCommand(cClose);
  1485.  
  1486.         // changed documents can be saved, but only if the file is open for write
  1487.         if (     (pData->changed) && 
  1488.                 ((pData->isWritable) || (pData->dataRefNum == -1)) )
  1489.             EnableCommand(cSave);
  1490.         
  1491.         // objects with a print method can be printed and page setup-ed
  1492.         if (pData->pPrintPage)
  1493.             {
  1494.             EnableCommand(cPrint);
  1495.             EnableCommand(cPageSetup);
  1496.             EnableCommand(cPrintOneCopy);
  1497.             }
  1498.             
  1499.         // let object enable anything else that needs to be enabled
  1500.         if (pData->pAdjustMenus)
  1501.             (*(pData->pAdjustMenus)) (pWindow, pData);
  1502.         }
  1503.         
  1504.     // Now determine if any of the menus have changed state
  1505.     {
  1506.     Boolean gotToRedraw = false;
  1507.     
  1508.     for (whichMenu = mApple; whichMenu <= mLastMenu; ++whichMenu)
  1509.         {
  1510.         menu = GetMenuHandle(whichMenu);
  1511.     
  1512.         if (menu)        // because contents menu may not be around
  1513.             {
  1514.             // If any of the menu is enabled 
  1515.             if ((**menu).enableFlags != 0)
  1516.                 {
  1517.                 // Make sure to turn on the menu title 
  1518.                 (**menu).enableFlags |= 1;
  1519.                 }
  1520.                 
  1521.             /*     If this new state is different than the saved state, then the menu bar
  1522.                 will need to be redrawn */
  1523.             if (wasEnabled[mLastMenu - whichMenu] != ((**menu).enableFlags && 1))
  1524.                 {
  1525.                 gotToRedraw = true;
  1526.                 }
  1527.             }
  1528.         }
  1529.         
  1530.     // And if any titles have changed state, redraw them 
  1531.     if (gotToRedraw)
  1532.         DrawMenuBar();
  1533.     }
  1534.         
  1535. } // AdjustMenus
  1536.  
  1537. // --------------------------------------------------------------------------------------------------------------
  1538. // FILE UTILITY ROUTINES
  1539. // --------------------------------------------------------------------------------------------------------------
  1540. #pragma segment Main
  1541.  
  1542. static Boolean BringToFrontIfOpen(FSSpecPtr pSpec)
  1543. {
  1544.     WindowRef        pWindow;
  1545.     
  1546.     pWindow = FrontWindow();
  1547.     while (pWindow)
  1548.         {
  1549.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1550.         
  1551.         if (
  1552.             (pData) &&
  1553.             (pData->fileSpec.vRefNum == pSpec->vRefNum) &&
  1554.             (pData->fileSpec.parID == pSpec->parID) &&
  1555.             EqualString(pData->fileSpec.name, pSpec->name, false, false)
  1556.             )
  1557.             {
  1558.             SelectWindow(pWindow);
  1559.             return true;
  1560.             }
  1561.             
  1562.         pWindow = GetNextWindow(pWindow);
  1563.         }
  1564.         
  1565.     return false;
  1566.     
  1567. } // BringToFrontIfOpen
  1568.  
  1569. // --------------------------------------------------------------------------------------------------------------
  1570. #pragma segment Main
  1571.  
  1572. static Boolean BringToFrontIfExists(ResType windowKind)
  1573. {
  1574.     WindowRef        pWindow;
  1575.     
  1576.     pWindow = FrontWindow();
  1577.     while (pWindow)
  1578.         {
  1579.         WindowDataPtr pData = GetWindowInfo(pWindow);
  1580.         
  1581.         if ((pData) && (pData->windowKind == windowKind))
  1582.             {
  1583.             SelectWindow(pWindow);
  1584.             return true;
  1585.             }
  1586.             
  1587.         pWindow = GetNextWindow(pWindow);
  1588.         }
  1589.         
  1590.     return false;
  1591.     
  1592. } // BringToFrontIfExists
  1593.  
  1594. // --------------------------------------------------------------------------------------------------------------
  1595. // MAIN SIMPLETEXT ROUTINES
  1596. // --------------------------------------------------------------------------------------------------------------
  1597. #pragma segment Main
  1598.  
  1599. static OSErr MakeNewWindow(ResType windowKind, FSSpecPtr fileSpec, OSType fileType, Boolean *pWasAlreadyOpen)
  1600. {
  1601.     OSErr                anErr = fnfErr;
  1602.     PreflightRecord        thePreflight;
  1603.     PreflightWindowProc    pPreflight = nil;
  1604.     WindowRef            pWindow;
  1605.     WindowDataPtr        pData;
  1606.     
  1607.     // require a certain amount of RAM free before we allow the new window to be created
  1608.     if (FreeMem() < kRAMNeededForNew)
  1609.         anErr = memFullErr;
  1610.         
  1611.     // <50> if we already have a document open from this file, bring the window to the
  1612.     // front and return with no error
  1613.     if ( (fileSpec) && (fileType != 'sEXT') && (BringToFrontIfOpen(fileSpec)) )
  1614.         {
  1615.         if (pWasAlreadyOpen)
  1616.             *pWasAlreadyOpen = true;
  1617.         anErr = noErr;
  1618.         return(anErr);
  1619.         }
  1620.     if (pWasAlreadyOpen)
  1621.         *pWasAlreadyOpen = false;
  1622.     if (anErr != fnfErr)
  1623.         {
  1624.         nrequire(anErr, SanityCheckFailed);
  1625.         }
  1626.         
  1627.     // initialize our behavior
  1628.     thePreflight.continueWithOpen     = true;
  1629.     thePreflight.resourceID         = kDefaultWindowID;
  1630.     thePreflight.wantHScroll         = false;
  1631.     thePreflight.wantVScroll         = false;
  1632.     thePreflight.storageSize         = sizeof(WindowDataRecord);
  1633.     thePreflight.makeProcPtr         = nil;
  1634.     thePreflight.openKind            = fsRdPerm;
  1635.     thePreflight.needResFork        = false;
  1636.     thePreflight.doZoom                = false;
  1637.     thePreflight.fileType            = fileType;
  1638.     
  1639.     switch (windowKind)
  1640.         {
  1641.         case kAboutWindow:
  1642.             pPreflight = AboutPreflightWindow;
  1643.             break;
  1644.  
  1645.         case kPICTWindow:
  1646.             pPreflight = PICTPreflightWindow;
  1647.             break;
  1648.  
  1649.         case kMovieWindow:
  1650.             pPreflight = MoviePreflightWindow;
  1651.             break;
  1652.  
  1653.         case kClipboardWindow:
  1654.             pPreflight = ClipboardPreflightWindow;
  1655.             break;
  1656.  
  1657.         case kTextWindow:
  1658.             pPreflight = TextPreflightWindow;
  1659.             break;
  1660.  
  1661.         case kThreeDWindow:
  1662.             pPreflight = ThreeDPreflightWindow;
  1663.             break;
  1664.         }
  1665.     
  1666.     // preflight the window    
  1667.     if (pPreflight)
  1668.         anErr = (*pPreflight) (&thePreflight);
  1669.     nrequire(anErr, PreflightFailed);
  1670.     
  1671.     if (thePreflight.continueWithOpen)
  1672.         {
  1673.         // allocate a place for the window
  1674.         pData = (WindowDataPtr)NewPtrClear(thePreflight.storageSize);
  1675.         anErr = MemError();
  1676.         nrequire(anErr, FailedToAllocateWindow);
  1677.         
  1678.         // then actually create the window
  1679.         if (gMachineInfo.theEnvirons.hasColorQD)
  1680.             pWindow = (WindowRef)GetNewCWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  1681.         else
  1682.             pWindow = (WindowRef)GetNewWindow(thePreflight.resourceID, pData, (WindowPtr)-1);
  1683.         if (!pWindow) anErr = memFullErr;
  1684.         nrequire(anErr, NewWindowFailed);
  1685.         SetWRefCon(pWindow, (long) pData);
  1686.                 
  1687.         // zoom the rectangle to big size on this monitor 
  1688.         // based upon which scroll bars they want
  1689.         {
  1690.         Rect    rect = GetWindowPort(pWindow)->portRect;
  1691.         Rect    bigRect;
  1692.         
  1693.         if (gMachineInfo.theEnvirons.hasColorQD)
  1694.             bigRect = (**GetMainDevice()).gdRect;
  1695.         else
  1696.             bigRect = qd.screenBits.bounds;
  1697.         bigRect.top += GetMBarHeight() * 2;
  1698.         bigRect.left += 4;
  1699.         bigRect.bottom -= 4;
  1700.         bigRect.right -= 65;
  1701.         
  1702.         SetPort((GrafPtr) GetWindowPort(pWindow));
  1703.         LocalToGlobal(&TopLeft(rect));
  1704.         LocalToGlobal(&BotRight(rect));
  1705.         
  1706.         if ( (thePreflight.wantHScroll) || (thePreflight.doZoom) )
  1707.             {
  1708.             rect.left = bigRect.left;
  1709.             rect.right = bigRect.right;
  1710.             }
  1711.             
  1712.         if ( (thePreflight.wantVScroll) || (thePreflight.doZoom) )
  1713.             {
  1714.             rect.top = bigRect.top;
  1715.             rect.bottom = bigRect.bottom;
  1716.             }
  1717.         
  1718.         MoveWindow(pWindow, rect.left, rect.top, false);
  1719.         SizeWindow(pWindow, rect.right - rect.left, rect.bottom - rect.top, false);
  1720.         }
  1721.         
  1722.         // fill in the default contents of the window
  1723.         pData->windowKind         = windowKind;
  1724.         pData->originalFileType    = fileType;
  1725.         pData->pMakeWindow         = (MakeWindowProc)thePreflight.makeProcPtr;
  1726.         pData->resRefNum        = -1;
  1727.         pData->dataRefNum        = -1;
  1728.         pData->contentRect         = GetWindowPort(pWindow)->portRect;
  1729.         
  1730.         // make the scroll bars
  1731.         {
  1732.         Rect    controlRect;
  1733.         
  1734.         if (thePreflight.wantHScroll)
  1735.             {
  1736.             pData->contentRect.bottom -= kScrollBarSize;
  1737.             controlRect = GetWindowPort(pWindow)->portRect;
  1738.             controlRect.top = controlRect.bottom - 16;
  1739.             if (thePreflight.wantVScroll)
  1740.                 controlRect.right -= kGrowScrollAdjust;
  1741.             OffsetRect(&controlRect, -1, 1);
  1742.             pData->hScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  1743.             }
  1744.         if (thePreflight.wantVScroll)
  1745.             {
  1746.             pData->contentRect.right -= kScrollBarSize;
  1747.             controlRect = GetWindowPort(pWindow)->portRect;
  1748.             controlRect.left = controlRect.right - 16;
  1749.             if (thePreflight.wantVScroll)
  1750.                 controlRect.bottom -= kGrowScrollAdjust;
  1751.             OffsetRect(&controlRect, 1, -1);
  1752.             pData->vScroll = NewControl(pWindow, &controlRect, "\p", true, 0, 0, 0, scrollBarProc, 0);
  1753.             }
  1754.         }
  1755.  
  1756.         // got a name?  Open the file        
  1757.         if (fileSpec)
  1758.             {
  1759.             anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  1760.             if     ( 
  1761.                     ((anErr == afpAccessDenied) || (anErr == opWrErr) || (anErr == permErr) ) && 
  1762.                     (thePreflight.openKind != fsRdPerm)
  1763.                 )
  1764.                 {
  1765.                 thePreflight.openKind = fsRdPerm;
  1766.                 pData->isWritable = false;
  1767.                 anErr = FSpOpenDF(fileSpec, thePreflight.openKind, &pData->dataRefNum);
  1768.                 }
  1769.             else
  1770.                 pData->isWritable = true;
  1771.             nrequire(anErr, FailedToOpenFile);
  1772.  
  1773.             // okay not to find a resource fork, because some don't have them                
  1774.             pData->resRefNum = FSpOpenResFile(fileSpec, thePreflight.openKind);
  1775.             
  1776.             // save the file spec in case someone is interested
  1777.             pData->fileSpec = *fileSpec;
  1778.             }
  1779.             
  1780.         if (pData->pMakeWindow)
  1781.             {
  1782.             Rect oldContent = pData->contentRect;
  1783.             anErr = (*(pData->pMakeWindow)) (pWindow, pData);
  1784.             if (!EqualRect(&oldContent, &pData->contentRect))
  1785.                 {
  1786.                 SizeWindow(pWindow, 
  1787.                         pData->contentRect.right  + (pData->vScroll != 0) * kScrollBarSize,
  1788.                         pData->contentRect.bottom + (pData->hScroll != 0) * kScrollBarSize,
  1789.                         false);
  1790.                 }
  1791.             }
  1792.         nrequire(anErr, FailedMakeWindow);
  1793.  
  1794.         // got a name?  Use it as the window title
  1795.         if ( (fileSpec) && (!pData->openAsNew) )
  1796.             SetWTitle(pWindow, fileSpec->name);
  1797.         else
  1798.             {
  1799.             if ((gMachineInfo.documentCount == 1) && (pData->windowKind == kTextWindow))
  1800.                 {
  1801.                 Str255 tempString;
  1802.         
  1803.                 GetIndString(tempString, kMiscStrings, iFirstNewDocumentTitle);    // get the "untitled" string (no number)
  1804.                 SetWTitle(pWindow, tempString);
  1805.                 }
  1806.             else
  1807.                 {
  1808.                 Str255    tempString;
  1809.                 Str32    numString;
  1810.     
  1811.                 GetWTitle(pWindow, tempString);
  1812.                 NumToString(gMachineInfo.documentCount, numString);
  1813.                 (void) ZeroStringSub(tempString, numString);
  1814.                 SetWTitle(pWindow, tempString);
  1815.                 }
  1816.  
  1817.             if (pData->bumpUntitledCount)
  1818.                 gMachineInfo.documentCount++;    // bump count if appropriate for this kind of document
  1819.             }
  1820.  
  1821.         // Make sure the scroll bars are reasonable in size, and move if they must
  1822.         AdjustScrollBars(pWindow, true, true, nil);
  1823.  
  1824.         // finally, if all goes well, we can see the window itself!
  1825.         ShowWindow(pWindow);
  1826.         }
  1827.  
  1828.     return noErr;
  1829.  
  1830. // EXCEPTION HANDLING
  1831. FailedMakeWindow:
  1832.     if (pData->resRefNum != -1)
  1833.         CloseResFile(pData->resRefNum);
  1834.     if (pData->dataRefNum != -1)
  1835.         FSClose(pData->dataRefNum);
  1836.     
  1837. FailedToOpenFile:
  1838.     CloseWindow(pWindow);
  1839.     
  1840. NewWindowFailed:
  1841.     DisposePtr((Ptr)pData);
  1842.     
  1843. FailedToAllocateWindow:
  1844. PreflightFailed:
  1845. SanityCheckFailed:
  1846.     return anErr;
  1847.     
  1848. } // MakeNewWindow
  1849.  
  1850. // --------------------------------------------------------------------------------------------------------------
  1851. #pragma segment Main
  1852.  
  1853. #define dontSaveChanges 3
  1854.  
  1855. #define kVisualDelay 8
  1856.  
  1857. static pascal Boolean SaveChangesFilter(DialogRef theDialog, EventRecord *theEvent, short *itemHit)
  1858. {
  1859.     if (StdFilterProc(theDialog, theEvent, itemHit))
  1860.         return true;
  1861.  
  1862.     if (theEvent->what == updateEvt)
  1863.         {
  1864.         HandleEvent(theEvent);
  1865.         }
  1866.  
  1867.     if (theEvent->what == keyDown)
  1868.         {
  1869.         StringPtr keyEquivs = *GetString(kSaveChangesWindowID);
  1870.         unsigned char theKey = theEvent->message & charCodeMask;
  1871.  
  1872.         if (keyEquivs && (theKey == keyEquivs[1] || theKey == keyEquivs[2]))
  1873.             {
  1874.             short itemType;
  1875.             Rect theRect;
  1876.             ControlRef theControl;
  1877.             unsigned long finalTicks;
  1878.  
  1879.             GetDialogItem(theDialog, dontSaveChanges, &itemType, (Handle *) &theControl, &theRect);
  1880.             HiliteControl(theControl, kControlButtonPart);
  1881.             Delay(kVisualDelay, &finalTicks);
  1882.             HiliteControl(theControl, 0);
  1883.  
  1884.             *itemHit = dontSaveChanges;
  1885.             return true;
  1886.             }
  1887.         }
  1888.  
  1889.     return false;
  1890. }
  1891.  
  1892.  
  1893. static OSErr DoCloseWindow(WindowRef pWindow)
  1894. {
  1895.     OSErr            anErr = noErr;
  1896.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  1897.     
  1898.     if ( (pData) && (pData->changed) )
  1899.         {
  1900.         short         hit;
  1901.         Str255        wTitle;
  1902.         DialogRef    dPtr;
  1903.         
  1904.         GetWTitle(pWindow, wTitle);
  1905.         SetCursor(&qd.arrow);
  1906.         ParamText(wTitle, "\p", "\p", "\p");
  1907.         
  1908.         hit = cancel;
  1909.         dPtr = GetNewDialog(kSaveChangesWindowID, nil, (WindowRef)-1);
  1910.         if (dPtr)
  1911.             {
  1912.             SetDialogDefaultItem(dPtr, ok);
  1913.             SetDialogCancelItem (dPtr, cancel);
  1914.             BeginMovableModal();
  1915.             do
  1916.                 {
  1917.                 MovableModalDialog(SaveChangesFilter, &hit);
  1918.                 } while ((hit != dontSaveChanges) && (hit != ok) && (hit != cancel));
  1919.                 
  1920.             DisposeDialog(dPtr);
  1921.             EndMovableModal();
  1922.             }
  1923.         switch (hit)
  1924.             {
  1925.             case ok:
  1926.                 anErr = DoCommand(pWindow, cSave, 0);
  1927.                 if (anErr == eUserCanceled)        // redundant?
  1928.                     gAllDone = false;
  1929.                 break;
  1930.                 
  1931.             case cancel:
  1932.                 anErr = eUserCanceled;
  1933.                 gAllDone = false;
  1934.                 break;
  1935.                 
  1936.             case dontSaveChanges:
  1937.                 // don't save, so just close it
  1938.                 break;
  1939.             }
  1940.         }
  1941.  
  1942.     if (anErr == noErr)
  1943.         {
  1944.         if ( (pData) && (pData->pCloseWindow) )
  1945.             {
  1946.             // let the object close the window if it wishes to
  1947.             anErr = (*(pData->pCloseWindow)) (pWindow, pData);
  1948.             }
  1949.  
  1950.         // otherwise we close it the default way
  1951.         if (anErr == noErr)
  1952.             {
  1953.             if (pData)
  1954.                 {
  1955.                 CloseWindow(pWindow);
  1956.     
  1957.                 if (pData->hPrint)
  1958.                     {
  1959.                     DisposeHandle((Handle) pData->hPrint);
  1960.                     }
  1961.                     
  1962.                 if (pData->resRefNum != -1)
  1963.                     CloseResFile(pData->resRefNum);
  1964.                 if (pData->dataRefNum != -1)
  1965.                     FSClose(pData->dataRefNum);
  1966.                 DisposePtr((Ptr) pData);
  1967.                 }
  1968.             }
  1969.         }
  1970.  
  1971.     // If we closed the last window, clean up
  1972.     if (FrontWindow() == nil)
  1973.         {
  1974.         AdjustMenus(nil, true, false);
  1975.         gMachineInfo.documentCount = 1;        // back to "untitled"
  1976.         }
  1977.     
  1978.     return anErr;
  1979.     
  1980. } // DoCloseWindow
  1981.  
  1982. #undef dontSaveChanges
  1983.  
  1984. // --------------------------------------------------------------------------------------------------------------
  1985. #pragma segment Main
  1986.  
  1987. static OSErr    DetermineWindowTypeOrOpen(
  1988.     FSSpecPtr theSpec, OSType theType,                 // optional input params -- file to open
  1989.     OSType *returnedTypeList, short * pNumTypes,    // optional input params -- returns list of files
  1990.     Boolean *pWasAlreadyOpen)                        // optional input params -- was file already open
  1991. {
  1992.     OSErr        anErr = noErr;
  1993.     OSType        typeList[20];
  1994.     OSType        docList[20];
  1995.     short        numTypes;
  1996.  
  1997.     // use local copies if the input params are nil    
  1998.     if (returnedTypeList == nil)
  1999.         returnedTypeList = &typeList[0];
  2000.     if (pNumTypes == nil)
  2001.         pNumTypes = &numTypes;
  2002.     *pNumTypes = 0;
  2003.     
  2004.     // Load up all of the file types we know how to handle
  2005.     AboutGetFileTypes(returnedTypeList, docList, pNumTypes);
  2006.     PICTGetFileTypes(returnedTypeList, docList, pNumTypes);
  2007.     MovieGetFileTypes(returnedTypeList, docList, pNumTypes);
  2008.     ClipboardGetFileTypes(returnedTypeList, docList, pNumTypes);
  2009.     TextGetFileTypes(returnedTypeList, docList, pNumTypes);
  2010.     ThreeDGetFileTypes(returnedTypeList, docList, pNumTypes);
  2011.  
  2012.     if (theSpec != nil)
  2013.         {
  2014.         short         index;
  2015.         OSType        windowType = '????';
  2016.  
  2017.         for (index = 0; index < (*pNumTypes); ++index)
  2018.             if (theType == returnedTypeList[index])
  2019.                 windowType = docList[index];
  2020.                 
  2021.         if (windowType != '????')
  2022.             {
  2023.             
  2024.             if ( (theType == 'TEXT') || (theType == 'sEXT') )
  2025.                 {
  2026.                 FInfo    theInfo;
  2027.                 
  2028.                 FSpGetFInfo(theSpec, &theInfo);
  2029.                 if ((theInfo.fdFlags & kIsStationery) != 0)
  2030.                     theType = 'sEXT';
  2031.                 else
  2032.                     theType = 'TEXT';
  2033.                 }
  2034.                 
  2035.             anErr = MakeNewWindow(windowType, theSpec, theType, pWasAlreadyOpen);
  2036.             }
  2037.         else
  2038.             anErr = eDocumentWrongKind;
  2039.         }
  2040.         
  2041.         
  2042.     return anErr;
  2043.     
  2044. } // DetermineWindowTypeOrOpen
  2045.  
  2046. // --------------------------------------------------------------------------------------------------------------
  2047. #pragma segment Main
  2048.  
  2049. // Handle update/activate events behind Standard File
  2050. static pascal Boolean OpenDialogFilter(DialogRef theDialog, EventRecord *theEvent,
  2051.                                       short *itemHit, void *myDataPtr)
  2052. {
  2053.     #pragma unused(myDataPtr)
  2054.  
  2055.     // Pass updates through (Activates are tricky...was mucking with Apple menu & thereby
  2056.     // drastically changing how the system handles the menu bar during our alert)
  2057.     if ( (theEvent->what == updateEvt) && (theEvent->message != (long)theDialog) )
  2058.         HandleEvent(theEvent);
  2059.  
  2060.     if (StdFilterProc(theDialog, theEvent, itemHit))
  2061.         return(true);
  2062.  
  2063.     return(false);
  2064.  
  2065. } // OpenDialogFilter
  2066.  
  2067. #if GENERATINGCFM
  2068.     static RoutineDescriptor gOpenDialogFilterRD = BUILD_ROUTINE_DESCRIPTOR(uppModalFilterYDProcInfo, OpenDialogFilter);
  2069.     static ModalFilterYDUPP gOpenDialogFilter = &gOpenDialogFilterRD;
  2070. #else
  2071.     static ModalFilterYDUPP gOpenDialogFilter = NewModalFilterYDProc(OpenDialogFilter);
  2072. #endif
  2073.  
  2074.  
  2075. static OSErr DoOpenWindow(void)
  2076. {
  2077.     OSErr                anErr = noErr;
  2078.     short                numTypes;
  2079.     OSType                typeList[20];
  2080.     StandardFileReply    sfReply;
  2081.     Point thePoint = { -1, -1 };
  2082.  
  2083.     DetermineWindowTypeOrOpen(nil, '????', &typeList[0], &numTypes, nil);
  2084.     
  2085.     if (gMachineInfo.haveQuickTime)
  2086.         {
  2087.         CustomGetFilePreview(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2088.         }
  2089.     else
  2090.         {
  2091.         CustomGetFile(nil, numTypes, typeList, &sfReply, 0, thePoint, nil, gOpenDialogFilter, nil, nil, nil);
  2092.         }
  2093.     
  2094.     if (sfReply.sfGood)
  2095.         {
  2096.         SetWatchCursor();
  2097.         
  2098.         anErr = DetermineWindowTypeOrOpen(&sfReply.sfFile, sfReply.sfType, &typeList[0], &numTypes, nil);
  2099.  
  2100.         SetCursor(&qd.arrow);
  2101.         }
  2102.         
  2103.     return anErr;
  2104.     
  2105. } // DoOpenWindow
  2106.  
  2107. // --------------------------------------------------------------------------------------------------------------
  2108. #pragma segment Main
  2109.  
  2110. static OSErr DoUpdateWindow(WindowRef pWindow)
  2111. {
  2112.     OSErr            anErr = noErr;
  2113.     WindowDataPtr    pData = GetWindowInfo(pWindow);
  2114.     GrafPtr            curPort;
  2115.     
  2116.     // only handle updates for windows we know about
  2117.     if (pData)
  2118.         {
  2119.         GetPort(&curPort);
  2120.         SetPort((GrafPtr)GetWindowPort(pWindow));
  2121.         BeginUpdate(pWindow);
  2122.                     
  2123.         if (pData->pUpdateWindow)
  2124.             anErr = (*(pData->pUpdateWindow)) (pWindow, pData);
  2125.     
  2126.         EndUpdate(pWindow);
  2127.         SetPort(curPort);
  2128.         }
  2129.     
  2130.     return anErr;
  2131.     
  2132. } // DoUpdateWindow
  2133.  
  2134. // --------------------------------------------------------------------------------------------------------------
  2135. #pragma segment Main
  2136.  
  2137. OSErr DoScrollContent(WindowRef pWindow, WindowDataPtr pData, short deltaH, short deltaV)
  2138. {
  2139.     OSErr    anErr = noErr;
  2140.     
  2141.     if ((deltaH) || (deltaV))
  2142.         {
  2143.         // if we have a balloon displayed, update before scrolling anything
  2144.         if (HMIsBalloon())
  2145.             DoUpdateWindow(pWindow);
  2146.         
  2147.         if ((pData) && (pData->pScrollContent))
  2148.             anErr = (*(pData->pScrollContent)) (pWindow, pData, deltaH, deltaV);
  2149.             
  2150.         if (anErr == noErr)
  2151.             {
  2152.             RgnHandle    invalidRgn = NewRgn();
  2153.             
  2154.             ScrollRect(&pData->contentRect, deltaH, deltaV, invalidRgn);
  2155.             InvalRgn(invalidRgn);
  2156.             DisposeRgn(invalidRgn);
  2157.     
  2158.             DoUpdateWindow(pWindow);
  2159.             }
  2160.         }
  2161.     
  2162.     return anErr;
  2163.     
  2164. } // DoScrollContent
  2165.  
  2166. // --------------------------------------------------------------------------------------------------------------
  2167. // BEGIN SCROLL ACTION PROCS
  2168. // --------------------------------------------------------------------------------------------------------------
  2169. #pragma segment Main
  2170.  
  2171. void SetControlAndClipAmount(ControlRef control, short * amount)
  2172. {
  2173.     short        value, max;
  2174.     
  2175.     value = GetControlValue(control);    /* get current value */
  2176.     max = GetControlMaximum(control);        /* and maximum value */
  2177.     *amount = value - *amount;
  2178.     if ( *amount < 0 )
  2179.         *amount = 0;
  2180.     else
  2181.         {
  2182.         if ( *amount > max )
  2183.             *amount = max;
  2184.         }
  2185.     SetControlValue(control, *amount);
  2186.     *amount = value - *amount;        /* calculate the real change */
  2187.     
  2188. } // SetControlAndClipAmount
  2189.  
  2190. // --------------------------------------------------------------------------------------------------------------
  2191. static pascal void VActionProc(ControlRef control, short part)
  2192. {
  2193.     if (part != 0)
  2194.         {
  2195.         WindowRef        pWindow = (**control).contrlOwner;
  2196.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2197.         short            amount = 0;
  2198.         
  2199.         switch (part)
  2200.             {
  2201.             case kControlUpButtonPart:
  2202.                 amount = pData->vScrollAmount;
  2203.                 break;
  2204.                 
  2205.             case kControlDownButtonPart:
  2206.                 amount = -pData->vScrollAmount;
  2207.                 break;
  2208.                 
  2209.             // vertical page scrolling should be a multiple of the incremental scrolling -- so that
  2210.             // we avoid half-lines of text at the bottom of pages.
  2211.             
  2212.             // More generically, if there was a method for dealing with text scrolling by a non-constant
  2213.             // amount, this would be better -- but SimpleText currently doesn't have a framework to allow
  2214.             // the document object to override the scroll amount dynamically.  Maybe something to add in
  2215.             // the future.
  2216.             case kControlPageUpPart:
  2217.                 amount = (((pData->contentRect.bottom - pData->contentRect.top) / pData->vScrollAmount)-1) * pData->vScrollAmount;
  2218.                 if (amount == 0)
  2219.                     amount = pData->contentRect.bottom - pData->contentRect.top;
  2220.                 break;
  2221.  
  2222.             case kControlPageDownPart:
  2223.                 amount = (((pData->contentRect.top - pData->contentRect.bottom) / pData->vScrollAmount)+1) * pData->vScrollAmount;
  2224.                 if (amount == 0)
  2225.                     amount = pData->contentRect.top - pData->contentRect.bottom;
  2226.                 break;
  2227.             }
  2228.         
  2229.         SetControlAndClipAmount(control, &amount);
  2230.         if (amount != 0)
  2231.             DoScrollContent(pWindow, pData, 0, amount);
  2232.         }
  2233.         
  2234. } // VActionProc
  2235.  
  2236. #if GENERATINGCFM
  2237.     static RoutineDescriptor gVActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, VActionProc);
  2238.     static ControlActionUPP gVActionProc = &gVActionProcRD;
  2239. #else
  2240.     static ControlActionUPP gVActionProc = VActionProc;
  2241. #endif
  2242.  
  2243. // --------------------------------------------------------------------------------------------------------------
  2244. static pascal void HActionProc(ControlRef control, short part)
  2245. {
  2246.     if (part != 0)
  2247.         {
  2248.         WindowRef        pWindow = (**control).contrlOwner;
  2249.         WindowDataPtr     pData = GetWindowInfo(pWindow);
  2250.         short            amount = 0;
  2251.         
  2252.         switch (part)
  2253.             {
  2254.             case kControlUpButtonPart:
  2255.                 amount = pData->hScrollAmount;
  2256.                 break;
  2257.                 
  2258.             case kControlDownButtonPart:
  2259.                 amount = -pData->hScrollAmount;
  2260.                 break;
  2261.                 
  2262.             case kControlPageUpPart:
  2263.                 amount = pData->contentRect.right - pData->contentRect.left;
  2264.                 break;
  2265.  
  2266.             case kControlPageDownPart:
  2267.                 amount = pData->contentRect.left - pData->contentRect.right;
  2268.                 break;
  2269.             }
  2270.         
  2271.         SetControlAndClipAmount(control, &amount);
  2272.         if (amount != 0)
  2273.             DoScrollContent(pWindow, pData, amount, 0);
  2274.         }
  2275.         
  2276. } // HActionProc
  2277.  
  2278. #if GENERATINGCFM
  2279.     static RoutineDescriptor gHActionProcRD = BUILD_ROUTINE_DESCRIPTOR(uppControlActionProcInfo, HActionProc);
  2280.     static ControlActionUPP gHActionProc = &gHActionProcRD;
  2281. #else
  2282.     static ControlActionUPP gHActionProc = HActionProc;
  2283. #endif
  2284.  
  2285. // --------------------------------------------------------------------------------------------------------------
  2286. // END SCROLL ACTION PROCS
  2287. // --------------------------------------------------------------------------------------------------------------
  2288.  
  2289. #pragma segment Main
  2290.  
  2291. static OSErr DoContentClick(WindowRef pWindow)
  2292. {
  2293.     OSErr            anErr = noErr;
  2294.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2295.     
  2296.     
  2297.     if ( pData )
  2298.         {
  2299.         SetPort((GrafPtr) GetWindowPort(pWindow));
  2300.         
  2301.         if (pData->pContentClick)
  2302.             {
  2303.             // let the object handle the click if it wishes to
  2304.             anErr = (*(pData->pContentClick)) (pWindow, pData, &gEvent);
  2305.             }
  2306.         
  2307.         if (anErr == noErr) 
  2308.             {
  2309.             ControlRef        theControl;
  2310.             short            part;
  2311.             
  2312.             GlobalToLocal(&gEvent.where);
  2313.             part = FindControl(gEvent.where, pWindow, &theControl);
  2314.             switch (part)
  2315.                 {
  2316.                 // do nothing for viewRect case
  2317.                 case 0:
  2318.                     break;
  2319.  
  2320.                 // track the thumb, and then update all at once
  2321.                 case kControlIndicatorPart:
  2322.                     {
  2323.                     short    value = GetControlValue(theControl);
  2324.                     
  2325.                     part = TrackControl(theControl, gEvent.where, nil);
  2326.                     if (part != 0)
  2327.                         {
  2328.                         // turn the value into a delta
  2329.                         value -= GetControlValue(theControl);
  2330.                         
  2331.                         // if we actually moved
  2332.                         if (value != 0)
  2333.                             {
  2334.                             if (theControl == pData->hScroll)
  2335.                                 DoScrollContent(pWindow, pData, value, 0);
  2336.                             if (theControl == pData->vScroll)
  2337.                                 DoScrollContent(pWindow, pData, 0, value);
  2338.                                 
  2339.                             }
  2340.                         }
  2341.                     }
  2342.                     break;
  2343.                     
  2344.                 // track the control, and scroll as we go
  2345.                 default:
  2346.                     if (theControl)
  2347.                         {
  2348.                         if (theControl == pData->hScroll)
  2349.                             part = TrackControl(theControl, gEvent.where, gHActionProc);
  2350.                         if (theControl == pData->vScroll)
  2351.                             part = TrackControl(theControl, gEvent.where, gVActionProc);
  2352.                         }
  2353.                     break;
  2354.                 }
  2355.             }
  2356.  
  2357.         }
  2358.  
  2359.         
  2360.     return anErr;
  2361.     
  2362. } // DoContentClick
  2363.  
  2364. // --------------------------------------------------------------------------------------------------------------
  2365. #pragma segment Main
  2366.  
  2367. static OSErr DoGrowWindow(WindowRef pWindow, EventRecord *pEvent)
  2368. {
  2369.     OSErr            anErr = noErr;
  2370.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2371.     Rect            tempRect;
  2372.     LongRect        docRect;
  2373.     long            growResult;
  2374.     
  2375.     if (pData)
  2376.         {
  2377.         GrafPtr    pPort = (GrafPtr)GetWindowPort(pWindow);
  2378.         
  2379.         SetPort(pPort);
  2380.         
  2381.         RectToLongRect(&pData->contentRect, &docRect);
  2382.         if (pData->pGetDocumentRect)
  2383.             (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2384.         if (pData->vScroll)
  2385.             docRect.right += 16;
  2386.         if (pData->hScroll)
  2387.             docRect.bottom += 16;
  2388.         
  2389.         if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2390.             {
  2391.             docRect.right += 16;
  2392.             docRect.bottom += 16;
  2393.             }
  2394.             
  2395.         // set up resize constraints
  2396.         tempRect.left = pData->minHSize;
  2397.         if (tempRect.left == 0)
  2398.             tempRect.left = kMinDocSize;
  2399.         tempRect.right = docRect.right - docRect.left;
  2400.         if (tempRect.right < tempRect.left)
  2401.             tempRect.right = tempRect.left;
  2402.         tempRect.top = pData->minVSize;
  2403.         if (tempRect.top == 0)
  2404.             tempRect.top = kMinDocSize;
  2405.         tempRect.bottom = docRect.bottom - docRect.top;
  2406.         if (tempRect.bottom < tempRect.top)
  2407.             tempRect.bottom = tempRect.top;
  2408.             
  2409.         growResult = GrowWindow(pWindow, pEvent->where, &tempRect);
  2410.         if ( growResult != 0 ) 
  2411.             {
  2412.             Rect        oldRect;
  2413.             RgnHandle    currentInval = NewRgn();
  2414.             Boolean        needInvalidate;
  2415.             
  2416.             // save old content area
  2417.             oldRect = pData->contentRect;
  2418.             
  2419.             // save old pending update
  2420.             GetWindowUpdateRgn(pWindow, currentInval);
  2421.             OffsetRgn(currentInval, pPort->portBits.bounds.left, pPort->portBits.bounds.top);
  2422.             
  2423.             // grow window and recalc what is needed
  2424.             SizeWindow(pWindow, growResult & 0xFFFF, growResult >> 16, true);
  2425.             AdjustScrollBars(pWindow, true, true, &needInvalidate);
  2426.             
  2427.             if (needInvalidate)
  2428.                 {
  2429.                 InvalRect(&pData->contentRect);
  2430.                 }
  2431.             else
  2432.                 {
  2433.                 // don't bother to redraw things that haven't changed
  2434.                 SectRect(&oldRect, &pData->contentRect, &oldRect);
  2435.                 ValidRect(&oldRect);
  2436.                 
  2437.                 // but, if a pending update was there, be sure to deal with that!
  2438.                 InvalRgn(currentInval);
  2439.                 }
  2440.  
  2441.             // if we have offset scrollbars, then force a redraw of them
  2442.             if (pData->hScrollOffset)
  2443.                 {
  2444.                 oldRect = GetWindowPort(pWindow)->portRect;
  2445.                 oldRect.right = oldRect.left + pData->hScrollOffset;
  2446.                 oldRect.top = oldRect.bottom - kScrollBarSize;
  2447.                 InvalRect(&oldRect);
  2448.                 }
  2449.             if (pData->vScrollOffset)
  2450.                 {
  2451.                 oldRect = GetWindowPort(pWindow)->portRect;
  2452.                 oldRect.bottom = oldRect.top + pData->vScrollOffset;
  2453.                 oldRect.left = oldRect.right - kScrollBarSize;
  2454.                 InvalRect(&oldRect);
  2455.                 }
  2456.  
  2457.             DisposeRgn(currentInval);
  2458.             }
  2459.             
  2460.         }
  2461.         
  2462.     
  2463.     return anErr;
  2464.     
  2465. } // DoGrowWindow
  2466.  
  2467. // --------------------------------------------------------------------------------------------------------------
  2468. #pragma segment Main
  2469.  
  2470. static OSErr DoZoomWindow(WindowRef pWindow, short zoomDir)
  2471. {
  2472.     Rect                *windRect, *zoomRect;
  2473.     Rect                globalPortRect, theSect, dGDRect;
  2474.     GDHandle            nthDevice, dominantGDevice;
  2475.     long                sectArea, greatestArea;
  2476.     short                 hMax, vMax;
  2477.     
  2478.     // determine the max size of the window
  2479.     {
  2480.     WindowDataPtr        pData = GetWindowInfo(pWindow);
  2481.     LongRect            docRect;
  2482.     
  2483.     RectToLongRect(&pData->contentRect, &docRect);
  2484.     if (pData->pGetDocumentRect)
  2485.         (*(pData->pGetDocumentRect)) (pWindow, pData, &docRect, true);
  2486.     if (pData->vScroll)
  2487.         docRect.right += kScrollBarSize;
  2488.     if (pData->hScroll)
  2489.         docRect.bottom += kScrollBarSize;
  2490.     
  2491.     if ( (pData->hasGrow) && (pData->hScroll == nil) && (pData->vScroll == nil) )
  2492.         {
  2493.         docRect.right += kScrollBarSize;
  2494.         docRect.bottom += kScrollBarSize;
  2495.         }
  2496.  
  2497.     hMax = docRect.right - docRect.left;
  2498.     vMax = docRect.bottom - docRect.top;
  2499.     }
  2500.     
  2501.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2502.     EraseRect(&GetWindowPort(pWindow)->portRect);    // recommended for cosmetic reasons
  2503.  
  2504.     if (zoomDir == inZoomOut) 
  2505.         {
  2506.  
  2507.         /*
  2508.          *    ZoomWindow() is a good basic tool, but it doesn't do everything necessary to
  2509.          *    implement a good human interface when zooming. In fact it's not even close for
  2510.          *    more high-end hardware configurations. We must help it along by calculating an
  2511.          *    appropriate window size and location any time a window zooms out.
  2512.          */
  2513.         {
  2514.         RgnHandle    structRgn = NewRgn();
  2515.         
  2516.         GetWindowStructureRgn(pWindow, structRgn);
  2517.         windRect = &(**structRgn).rgnBBox;
  2518.         DisposeRgn(structRgn);
  2519.         }
  2520.         dominantGDevice = nil;
  2521.         if (gMachineInfo.theEnvirons.hasColorQD) 
  2522.             {
  2523.  
  2524.             /*
  2525.              *    Color QuickDraw implies the possibility of multiple monitors. This is where
  2526.              *    zooming becomes more interesting. One should zoom onto the monitor containing
  2527.              *    the greatest portion of the window. This requires walking the gDevice list.
  2528.              */
  2529.  
  2530.             nthDevice = GetDeviceList();
  2531.             greatestArea = 0;
  2532.             while (nthDevice != nil) 
  2533.                 {
  2534.                 if (TestDeviceAttribute(nthDevice, screenDevice)) 
  2535.                     {
  2536.                     if (TestDeviceAttribute(nthDevice, screenActive)) 
  2537.                         {
  2538.                         SectRect(windRect, &(**nthDevice).gdRect, &theSect);
  2539.                         sectArea = (long) RectWidth(theSect) * (long) RectHeight(theSect);
  2540.                         if (sectArea > greatestArea) 
  2541.                             {
  2542.                             greatestArea = sectArea;        // save the greatest intersection
  2543.                             dominantGDevice = nthDevice;    // and which device it belongs to
  2544.                             }
  2545.                         }
  2546.                     }
  2547.                 nthDevice = GetNextDevice(nthDevice);
  2548.                 }
  2549.             }
  2550.  
  2551.         /*
  2552.          *    At this point, we know the dimensions of the window we're zooming, and we know
  2553.          *    what screen we're going to put it on. To be more specific, however, we need a
  2554.          *    rectangle which defines the maximum dimensions of the resized window's contents.
  2555.          *    This rectangle accounts for the thickness of the window frame, the menu bar, and
  2556.          *    one or two pixels around the edges for cosmetic compatibility with ZoomWindow().
  2557.          */
  2558.  
  2559.         if (dominantGDevice != nil) 
  2560.             {
  2561.             dGDRect = (**dominantGDevice).gdRect;
  2562.             if (dominantGDevice == GetMainDevice())        // account for menu bar on main device
  2563.                 dGDRect.top += GetMBarHeight();
  2564.             }
  2565.         else 
  2566.             {
  2567.             dGDRect = qd.screenBits.bounds;                // if no gDevice, use default monitor
  2568.             dGDRect.top += GetMBarHeight();
  2569.             }
  2570.  
  2571.         globalPortRect = GetWindowPort(pWindow)->portRect;
  2572.         LocalToGlobal(&TopLeft(globalPortRect));        // calculate the window's portRect
  2573.         LocalToGlobal(&BotRight(globalPortRect));        // in global coordinates
  2574.  
  2575.         // account for the window frame and inset it a few pixels
  2576.         dGDRect.left    += 2 + globalPortRect.left - windRect->left;
  2577.         dGDRect.top        += 2 + globalPortRect.top - windRect->top;
  2578.         dGDRect.right    -= 1 + windRect->right - globalPortRect.right;
  2579.         dGDRect.bottom    -= 1 + windRect->bottom - globalPortRect.bottom;
  2580.  
  2581.         /*
  2582.          *    Now we know exactly what our limits are, and since there are input parameters
  2583.          *    specifying the dimensions we'd like to see, we can move and resize the zoom
  2584.          *    state rectangle for the best possible results. We have three goals in this:
  2585.          *    1. Display the window entirely visible on a single device.
  2586.          *    2. Resize the window to best represent the dimensions of the document itself.
  2587.          *    3. Move the window as short a distance as possible to achieve #1 and #2.
  2588.          */
  2589.  
  2590.         zoomRect = &(**(WStateDataHandle) ((WindowPeek) pWindow)->dataHandle).stdState;
  2591.  
  2592.         /*
  2593.          *    Initially set the zoom rectangle to the size requested by the input parameters,
  2594.          *    although not smaller than a minimum size. We do this without moving the origin.
  2595.          */
  2596.  
  2597.         zoomRect->right = (zoomRect->left = globalPortRect.left) +
  2598.                                 Max(hMax, kMinDocSize);
  2599.         zoomRect->bottom = (zoomRect->top = globalPortRect.top) +
  2600.                                 Max(vMax, kMinDocSize);
  2601.  
  2602.         // Shift the entire rectangle if necessary to bring its origin inside dGDRect.
  2603.         OffsetRect(zoomRect,
  2604.                     Max(dGDRect.left - zoomRect->left, 0),
  2605.                     Max(dGDRect.top - zoomRect->top, 0));
  2606.  
  2607.         /*
  2608.          *    Shift the rectangle up and/or to the left if necessary to accomodate the view,
  2609.          *    and if it is possible to do so. The rectangle may not be moved such that its
  2610.          *    origin would fall outside of dGDRect.
  2611.          */
  2612.  
  2613.         OffsetRect(zoomRect,
  2614.                     -Pin(zoomRect->right - dGDRect.right, 0, zoomRect->left - dGDRect.left),
  2615.                     -Pin(zoomRect->bottom - dGDRect.bottom, 0, zoomRect->top - dGDRect.top));
  2616.  
  2617.         // Clip expansion to dGDRect, in case view is larger than dGDRect.
  2618.         zoomRect->right = Min(zoomRect->right, dGDRect.right);
  2619.         zoomRect->bottom = Min(zoomRect->bottom, dGDRect.bottom);
  2620.         }
  2621.  
  2622.     ZoomWindow(pWindow, zoomDir, pWindow == FrontWindow());
  2623.     
  2624.     AdjustScrollBars(pWindow, true, true, nil);
  2625.  
  2626.     InvalRect(&GetWindowPort(pWindow)->portRect);
  2627.     
  2628.     return noErr;
  2629.     
  2630. } // DoZoomWindow
  2631.  
  2632. // --------------------------------------------------------------------------------------------------------------
  2633. #pragma segment Main
  2634.  
  2635. OSErr DoActivate(WindowRef pWindow, Boolean activating)
  2636. {
  2637.  
  2638.     OSErr            anErr = noErr;
  2639.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2640.     
  2641.     SetPort((GrafPtr) GetWindowPort(pWindow));
  2642.     
  2643.     if ( pData )
  2644.         {
  2645.         if (pData->pActivateEvent)
  2646.             anErr = (*(pData->pActivateEvent)) (pWindow, pData, activating);
  2647.             
  2648.         if (anErr == noErr)
  2649.             {
  2650.             if (activating)
  2651.                 {
  2652.                 // Reshow all controls on activation
  2653.                 if (pData->hScroll)
  2654.                     ShowControl(pData->hScroll);
  2655.                 if (pData->vScroll)
  2656.                     ShowControl(pData->vScroll);
  2657.                 }
  2658.             else
  2659.                 {
  2660.                 // Hide all controls on deactivation
  2661.                 if (pData->hScroll)
  2662.                     HideControl(pData->hScroll);
  2663.                 if (pData->vScroll)
  2664.                     HideControl(pData->vScroll);
  2665.                 }
  2666.                 
  2667.             if (pData->hasGrow) 
  2668.                 {
  2669.                 Rect    growIconRect;
  2670.                 
  2671.                 CalculateGrowIcon(pData, &growIconRect);
  2672.                 InvalRect(&growIconRect);
  2673.                 }
  2674.             }
  2675.         }
  2676.  
  2677.     AdjustMenus(pWindow, true, false);
  2678.         
  2679.     return anErr;
  2680.     
  2681. } // DoActivate
  2682.  
  2683. // --------------------------------------------------------------------------------------------------------------
  2684. #pragma segment Main
  2685.  
  2686. OSErr    DoDefault(WindowDataPtr     pData)
  2687. {
  2688.     OSErr    anErr = noErr;
  2689.     
  2690.     PrOpen();
  2691.     anErr = PrError();
  2692.     if (anErr == noErr)
  2693.         {
  2694.         if (pData->hPrint == nil)
  2695.             {
  2696.             pData->hPrint = NewHandleClear(sizeof(TPrint));
  2697.             anErr = MemError();
  2698.             if (anErr == noErr)
  2699.                 extendPrDefault(pData->hPrint);
  2700.             }
  2701.             
  2702.         }
  2703.     PrClose();
  2704.         
  2705.     return anErr;
  2706.     
  2707. } // DoDefault
  2708.  
  2709. // --------------------------------------------------------------------------------------------------------------
  2710. #pragma segment Main
  2711.  
  2712. OSErr    DoPageSetup(WindowRef pWindow)
  2713. {
  2714.     OSErr            anErr = noErr;
  2715.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2716.         
  2717.     anErr = DoDefault(pData);
  2718.     nrequire(anErr, DoDefault);
  2719.     
  2720.     PrOpen();
  2721.     anErr = PrError();
  2722.     if (anErr == noErr)
  2723.         {
  2724.         SetCursor(&qd.arrow);
  2725.         PrStlDialog(pData->hPrint);
  2726.         }
  2727.     PrClose();
  2728.  
  2729. // FALL THROUGH EXCEPTION HANDLING
  2730. DoDefault:        
  2731.     return anErr;
  2732.     
  2733. } // DoPageSetup
  2734.  
  2735. // --------------------------------------------------------------------------------------------------------------
  2736. #pragma segment Main
  2737.  
  2738. static OSErr    DoPrintSetup(WindowRef pWindow, StringPtr pPrinterName)
  2739. {
  2740.     OSErr            anErr = noErr;
  2741.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2742.         
  2743.     anErr = DoDefault(pData);
  2744.     nrequire(anErr, DoDefault);
  2745.     
  2746.     PrOpen();
  2747.     anErr = PrError();
  2748.     if (anErr == noErr)
  2749.         {
  2750.         SetCursor(&qd.arrow);
  2751.         if (PrJobDialog(pData->hPrint) == false)
  2752.             anErr = eUserCanceled;
  2753.         }
  2754.         
  2755.     PrClose();
  2756.         
  2757. // FALL THROUGH EXCEPTION HANDLING
  2758. DoDefault:        
  2759.     return anErr;
  2760.     
  2761. } // DoPrintSetup
  2762.  
  2763. // --------------------------------------------------------------------------------------------------------------
  2764. #pragma segment Main
  2765.  
  2766. // This function is a duplicate of DoPrintSetup, but without the DoDefault call
  2767. // that DoPrintSetup normally does. The reason for this is that with the addition
  2768. // of the scriptable printing, I may be passing a print record into this function
  2769. // which I want to feed into the print dialog. If that's the case, the DoDefault
  2770. // would wipe out that print record, which is not what I would want to do.
  2771. //
  2772. // Also, rather than modifying the DoPrintSetup function, I duplicate the function
  2773. // here to illustrate the change more clearly. This probably isn't how you would
  2774. // handle this sort of conflict (a function needing to change) in the real world,
  2775. // but I think this better shows what you need to change.
  2776. static OSErr    DoPrintSetupNoDefault(WindowRef pWindow, StringPtr pPrinterName)
  2777. {
  2778.     OSErr            anErr = noErr;
  2779.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  2780.  
  2781.     PrOpen();
  2782.     anErr = PrError();
  2783.     if (anErr == noErr)
  2784.         {
  2785.         SetCursor(&qd.arrow);
  2786.         if (PrJobDialog(pData->hPrint) == false)
  2787.             anErr = eUserCanceled;
  2788.         }
  2789.         
  2790.     PrClose();
  2791.         
  2792.     return anErr;
  2793.     
  2794. } // DoPrintSetupNoDefault
  2795.  
  2796. // --------------------------------------------------------------------------------------------------------------
  2797. #pragma segment Main
  2798.  
  2799. static OSErr    DoPrint(WindowRef pWindow, void * hPrint, Boolean oneCopy)
  2800. {
  2801.     OSErr                anErr = noErr;
  2802.     WindowDataPtr         pData = GetWindowInfo(pWindow);
  2803.     Boolean                didAllocate = false;
  2804.     TPPrPort            printingPort;
  2805.     
  2806.     // use a watch cursor while printing
  2807.     SetWatchCursor();
  2808.     
  2809.     PrOpen();
  2810.     anErr = PrError();
  2811.     if (anErr == noErr)
  2812.         {
  2813.         if (hPrint == nil)
  2814.             {
  2815.             hPrint = NewHandleClear(sizeof(TPrint));
  2816.             anErr = MemError();
  2817.             if (anErr == noErr)
  2818.                 {
  2819.                 extendPrDefault(hPrint);
  2820.                 didAllocate = true;
  2821.                 }
  2822.             }
  2823.         
  2824.         if (anErr == noErr)
  2825.             {
  2826.             short    firstPage, lastPage;
  2827.             
  2828.             // be sure to get the page range BEFORE calling PrValidate(), 
  2829.             // which blows it away for many drivers.
  2830.             firstPage = (**(THPrint)hPrint).prJob.iFstPage;
  2831.             lastPage = (**(THPrint)hPrint).prJob.iLstPage;
  2832.             
  2833.             // make sure the print record is cool to use
  2834.             extendPrValidate(hPrint);
  2835.  
  2836.             // then clear out the job itself -- some drivers don't
  2837.             // do this from within PrValidate().  We want the job
  2838.             // clear in case the driver bases background behavior
  2839.             // from this range (and many do).
  2840.             (**(THPrint)hPrint).prJob.iFstPage = 1;
  2841.             (**(THPrint)hPrint).prJob.iLstPage = 9999;
  2842.             
  2843.             if (oneCopy)
  2844.                 (** ((THPrint)hPrint)).prJob.iCopies = 1;
  2845.                 
  2846.             // start printing, and then loop over the pages
  2847.             printingPort = PrOpenDoc(hPrint, nil, nil);
  2848.             anErr = PrError();
  2849.             if (anErr == noErr)
  2850.                 {
  2851.                 long    pageIndex;
  2852.                 Rect    pageRect;
  2853.                 
  2854.                 SetPort((GrafPtr) printingPort);
  2855.                 
  2856.                 pageRect = (**(THPrint)hPrint).prInfo.rPage;
  2857.                 if (firstPage < 1)
  2858.                     firstPage = 1;
  2859.                 if (lastPage < firstPage)
  2860.                     lastPage = firstPage;
  2861.                 for (pageIndex = firstPage; pageIndex <= lastPage; ++pageIndex)
  2862.                     {
  2863.                     PrOpenPage(printingPort, nil);
  2864.                     anErr = PrError();
  2865.                     if (anErr == noErr)
  2866.                         anErr = (*(pData->pPrintPage)) (pWindow, pData, &pageRect, &pageIndex);
  2867.                         
  2868.                     PrClosePage(printingPort);
  2869.                     if (anErr == noErr)
  2870.                         anErr = PrError();
  2871.                     if ( (anErr != noErr) || (pageIndex == -1) )
  2872.                         break;
  2873.                     }
  2874.                 }
  2875.                 
  2876.             // Finish up printing of the document
  2877.             PrCloseDoc(printingPort);
  2878.             if (anErr == noErr)
  2879.                 anErr = PrError();
  2880.             if ( (anErr == noErr) && ((**(THPrint)hPrint).prJob.bJDocLoop == bSpoolLoop) )
  2881.                 {
  2882.                 TPrStatus    theStatus;
  2883.                 
  2884.                 PrPicFile(hPrint, nil, nil, nil, &theStatus);
  2885.                 anErr = PrError();
  2886.                 }
  2887.             }
  2888.             
  2889.         if (didAllocate)
  2890.             DisposeHandle((Handle) hPrint);
  2891.             
  2892.         }
  2893.     PrClose();
  2894.     
  2895.         
  2896.     // restore cursor
  2897.     SetCursor(&qd.arrow);
  2898.  
  2899.     return anErr;
  2900.     
  2901. } // DoPrint
  2902.  
  2903. // --------------------------------------------------------------------------------------------------------------
  2904. #pragma segment Main
  2905.  
  2906. OSErr    DoCommand(WindowRef pWindow, short commandID, long menuResult)
  2907. {
  2908.     OSErr            anErr = noErr;
  2909.     WindowDataPtr     pData = nil;
  2910.     
  2911.     if (pWindow)
  2912.         {
  2913.         pData = (WindowDataPtr) GetWindowInfo(pWindow);
  2914.         
  2915.         if ( (pData) && (pData->pCommand) )
  2916.             anErr = (*(pData->pCommand)) (pWindow, pData, commandID, menuResult);
  2917.         }
  2918.     
  2919.     if (anErr == noErr)
  2920.         {
  2921.         // default command handling
  2922.         switch (commandID)
  2923.             {
  2924.             // About box command
  2925.             case cAbout:
  2926.                 if (!BringToFrontIfExists(kAboutWindow))
  2927.                     anErr = MakeNewWindow(kAboutWindow, nil, '????', nil);
  2928.                 break;
  2929.                 
  2930.             case cDeskAccessory:
  2931.                 {
  2932.                 Str255    tempString;
  2933.                 
  2934.                 GetMenuItemText(GetMenuHandle(menuResult>>16), menuResult & 0xFFFF, tempString);
  2935.                 OpenDeskAcc(tempString);
  2936.                 }
  2937.                 break;
  2938.                 
  2939.             // New window command
  2940.             case cNew:
  2941.                 anErr = MakeNewWindow(kTextWindow, nil, 'TEXT', nil);
  2942.                 break;
  2943.                 
  2944.             // Open window command
  2945.             case cOpen:
  2946.                 anErr = DoOpenWindow();
  2947.                 break;
  2948.                 
  2949.             // Close window command
  2950.             case cClose:
  2951.                 anErr = DoCloseWindow(pWindow);
  2952.                 break;
  2953.                 
  2954.             case cPageSetup:
  2955.                 anErr = DoPageSetup(pWindow);
  2956.                 break;
  2957.                 
  2958.             case cPrint:
  2959.                 anErr = DoPrintSetup(pWindow, nil);
  2960.                 if (anErr == noErr) {
  2961.                     anErr = DoPrint(pWindow, pData->hPrint, false);
  2962.                 }
  2963.                 break;
  2964.                 
  2965.             case cPrintOneCopy:
  2966.                 anErr = DoPrint(pWindow, pData->hPrint, true);
  2967.                 break;
  2968.                 
  2969.             // get out of here command!
  2970.             case cQuit:
  2971.                 gAllDone = true;
  2972.                 break;
  2973.     
  2974.             // show/hide clipboard
  2975.             case cShowClipboard:
  2976.                 if (!BringToFrontIfExists(kClipboardWindow))
  2977.                     {
  2978.                     anErr = MakeNewWindow(kClipboardWindow, nil, '????', nil);
  2979.                     }
  2980.                 else
  2981.                     {
  2982.                     pWindow = FrontWindow();
  2983.                     anErr = DoCloseWindow(pWindow);
  2984.                     }
  2985.                 break;
  2986.                 
  2987.             case cNextPage:
  2988.                 gEvent.what = keyDown;
  2989.                 gEvent.message = kPageDown << 8;
  2990.                 gEvent.modifiers = 0;
  2991.                 DoKeyEvent(pWindow, &gEvent, false);
  2992.                 break;
  2993.                 
  2994.             case cPreviousPage:
  2995.                 gEvent.what = keyDown;
  2996.                 gEvent.message = kPageUp << 8;
  2997.                 gEvent.modifiers = 0;
  2998.                 DoKeyEvent(pWindow, &gEvent, false);
  2999.                 break;
  3000.                 
  3001.             // Do nothing command
  3002.             case cNull:
  3003.                 break;
  3004.                             
  3005.             default:
  3006.                 break;
  3007.             }
  3008.         }
  3009.         
  3010.     // don't report cancels
  3011.     if (anErr == iPrAbort)
  3012.         anErr = noErr;
  3013.         
  3014.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  3015.         {
  3016.         // some commands are so similar to other commands that we map their IDs
  3017.         // for the purposes of the error strings
  3018.         if (commandID == cSaveAs)
  3019.             commandID = cSave;
  3020.         if (commandID == cPrintOneCopy)
  3021.             commandID = cPrint;
  3022.             
  3023.         ConductErrorDialog(anErr, commandID, cancel);
  3024.         }
  3025.         
  3026.     // in any case, unhilite the menu selected after command processing is done    
  3027.     HiliteMenu(0);
  3028.     
  3029.     return anErr;
  3030.     
  3031. } // DoCommand
  3032.  
  3033. // --------------------------------------------------------------------------------------------------------------
  3034. #pragma segment Main
  3035.  
  3036. static OSErr    DoMenuCommand(WindowRef pWindow, long menuResult)
  3037. {
  3038.     OSErr    anErr = noErr;
  3039.     short    commandID = cNull;
  3040.     short    ** commandHandle;
  3041.     short    menuID = menuResult >> 16;
  3042.     
  3043.     if (menuID >= mFontSubMenusStart)
  3044.         {
  3045.         commandID = cSelectFontStyle;
  3046.         }
  3047.     else
  3048.         {
  3049.         // read in the resource that controls this menu
  3050.             {
  3051.             short    oldResFile = CurResFile();
  3052.             
  3053.             UseResFile(gApplicationResFile);
  3054.             commandHandle = (short**) Get1Resource('MCMD', menuID);
  3055.             UseResFile(oldResFile);
  3056.             anErr = ResError();
  3057.             nrequire(anErr, FailedToLoadCommandTable);
  3058.             }
  3059.         
  3060.         if (commandHandle)
  3061.             {
  3062.             short    item = menuResult & 0xFFFF;
  3063.             short    * pCommands = *commandHandle;
  3064.             
  3065.             if (item <= pCommands[0])
  3066.                 commandID = pCommands[item];
  3067.             else
  3068.                 commandID = pCommands[pCommands[0]];
  3069.             }
  3070.         }
  3071.     
  3072.     anErr = DoCommand(pWindow, commandID, menuResult);
  3073.     
  3074. // FALL THROUGH EXCEPTION HANDLING
  3075. FailedToLoadCommandTable:
  3076.  
  3077.     return anErr;
  3078.     
  3079. } // DoMenuCommand
  3080.  
  3081.  
  3082. // --------------------------------------------------------------------------------------------------------------
  3083. #pragma segment Main
  3084.  
  3085. static void DoKeyPageDown(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3086. {
  3087.  
  3088.     if (GetControlValue(pData->vScroll) < GetControlMaximum(pData->vScroll))
  3089.         VActionProc(pData->vScroll, kControlPageDownPart);
  3090.     else
  3091.         {
  3092.         if ( (processPageControls) && (IsCommandEnabled(cNextPage)) )
  3093.             {
  3094.             short amount;
  3095.  
  3096.             if (DoCommand(pWindow, cNextPage, 0) == eActionAlreadyHandled)
  3097.                 {
  3098.                 amount = GetControlValue(pData->vScroll);
  3099.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3100.                 if (amount != 0)
  3101.                     DoScrollContent(pWindow, pData, 0, amount);
  3102.                 }
  3103.             
  3104.             AdjustMenus(pWindow, true, false);
  3105.             }
  3106.         }
  3107.     
  3108. } // DoKeyPageDown
  3109.  
  3110. // --------------------------------------------------------------------------------------------------------------
  3111. #pragma segment Main
  3112.  
  3113. static void DoKeyPageUp(WindowRef pWindow, WindowDataPtr pData, Boolean processPageControls)
  3114. {
  3115.     if (GetControlValue(pData->vScroll) > GetControlMinimum(pData->vScroll))
  3116.         VActionProc(pData->vScroll, kControlPageUpPart);
  3117.     else
  3118.         {
  3119.         if ( (processPageControls) && (IsCommandEnabled(cPreviousPage)) )
  3120.             {
  3121.             short amount;
  3122.             
  3123.             if (DoCommand(pWindow, cPreviousPage, 0) == eActionAlreadyHandled)
  3124.                 {
  3125.                 amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3126.                 SetControlAndClipAmount(pData->vScroll, &amount);
  3127.                 if (amount != 0)
  3128.                     DoScrollContent(pWindow, pData, 0, amount);
  3129.                 }
  3130.             
  3131.             AdjustMenus(pWindow, true, false);
  3132.             }
  3133.         }
  3134.         
  3135. } // DoKeyPageUp
  3136.  
  3137. // --------------------------------------------------------------------------------------------------------------
  3138. #pragma segment Main
  3139.  
  3140. OSErr    DoKeyEvent(WindowRef pWindow, EventRecord * pEvent, Boolean processPageControls)
  3141. {
  3142.     OSErr            anErr = noErr;
  3143.     WindowDataPtr     pData = nil;
  3144.     Boolean            passToObject = false;
  3145.     Boolean         isMotionKey = false;
  3146.     long            menuResult = 0;
  3147.     
  3148.     char keyCode = (pEvent->message >> 8) & charCodeMask;
  3149.  
  3150.     if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3151.         {
  3152.         AdjustMenus(pWindow, true, false);
  3153.         menuResult = MenuKey(pEvent->message & charCodeMask);
  3154.         DoMenuCommand(pWindow, menuResult);
  3155.         pWindow = FrontWindow();
  3156.         }
  3157.  
  3158.     if (menuResult == 0)
  3159.         {
  3160.         if (pWindow)
  3161.             {
  3162.             pData = (WindowDataPtr)GetWindowInfo(pWindow);
  3163.             if ( (pData) && (pData->pKeyEvent) )
  3164.                 passToObject = true;
  3165.             SetPort((GrafPtr) GetWindowPort(pWindow));
  3166.             }
  3167.             
  3168.         if (pData)
  3169.             {
  3170.             switch (keyCode)
  3171.                 {
  3172.                 case kHome: // top of file
  3173.                     isMotionKey = true;
  3174.                     if (pData->vScroll)
  3175.                         {
  3176.                         short amount;
  3177.                         
  3178.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3179.                             DoCommand(pWindow, cGotoPage, cGotoFirst);
  3180.  
  3181.                         amount = GetControlValue(pData->vScroll);
  3182.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3183.                         if (amount != 0)
  3184.                             DoScrollContent(pWindow, pData, 0, amount);
  3185.                         passToObject = false;
  3186.                         }
  3187.                     break;
  3188.                     
  3189.                 case kEnd: // end of file
  3190.                     isMotionKey = true;
  3191.                     if (pData->vScroll)
  3192.                         {
  3193.                         short amount;
  3194.  
  3195.                         if ( (processPageControls) && (IsCommandEnabled(cGotoPage)) )
  3196.                             DoCommand(pWindow, cGotoPage, cGotoLast);
  3197.                             
  3198.                         amount = -(GetControlMaximum(pData->vScroll)-GetControlValue(pData->vScroll));
  3199.                         SetControlAndClipAmount(pData->vScroll, &amount);
  3200.                         if (amount != 0)
  3201.                             DoScrollContent(pWindow, pData, 0, amount);
  3202.                         passToObject = false;
  3203.                         }
  3204.                     break;
  3205.                     
  3206.                 case kPageUp: // scroll bar page up
  3207.                     isMotionKey = true;
  3208.                     if (pData->vScroll)
  3209.                         {
  3210.                         DoKeyPageUp(pWindow, pData, processPageControls);
  3211.                         passToObject = false;
  3212.                         }
  3213.                     break;
  3214.                     
  3215.                 case kPageDown: // scroll bar page down
  3216.                     isMotionKey = true;
  3217.                     if (pData->vScroll)
  3218.                         {
  3219.                         DoKeyPageDown(pWindow, pData, processPageControls);
  3220.                         passToObject = false;
  3221.                         }
  3222.                     break;
  3223.                             
  3224.                 case kUpArrow:        // scroll bar up arrow
  3225.                     isMotionKey = true;
  3226.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3227.                         {
  3228.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3229.                             DoKeyPageUp(pWindow, pData, processPageControls);
  3230.                         else
  3231.                             VActionProc(pData->vScroll, kControlUpButtonPart);
  3232.                         passToObject = false;
  3233.                         }
  3234.                     break;
  3235.                     
  3236.                 case kDownArrow:    // scroll bar down arrow
  3237.                     isMotionKey = true;
  3238.                     if ( (pData->vScroll) && (!pData->pKeyEvent) )
  3239.                         {
  3240.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3241.                             DoKeyPageDown(pWindow, pData, processPageControls);
  3242.                         else
  3243.                             VActionProc(pData->vScroll, kControlDownButtonPart);
  3244.                         passToObject = false;
  3245.                         }
  3246.                     break;
  3247.         
  3248.                 case kLeftArrow:    // scroll bar left arrow
  3249.                     isMotionKey = true;
  3250.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3251.                         {
  3252.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3253.                             HActionProc(pData->hScroll, kControlPageUpPart);
  3254.                         else
  3255.                             HActionProc(pData->hScroll, kControlUpButtonPart);
  3256.                         passToObject = false;
  3257.                         }
  3258.                     break;
  3259.                     
  3260.                 case kRightArrow:    // scroll bar right arrow
  3261.                     isMotionKey = true;
  3262.                     if ( (pData->hScroll) && (!pData->pKeyEvent) )
  3263.                         {
  3264.                         if ( pEvent->modifiers & cmdKey )            /* Command key down */
  3265.                             HActionProc(pData->hScroll, kControlPageDownPart);
  3266.                         else
  3267.                             HActionProc(pData->hScroll, kControlDownButtonPart);
  3268.                         passToObject = false;
  3269.                         }
  3270.                     break;
  3271.                     }
  3272.         
  3273.             if (passToObject)
  3274.                 anErr = (*(pData->pKeyEvent)) (pWindow, pData, pEvent, isMotionKey);
  3275.             else
  3276.                 {
  3277.                 if ( (pData->documentAcceptsText == false) && !( pEvent->modifiers & cmdKey ) && !(isMotionKey) )
  3278.                     anErr = eDocumentNotModifiable;
  3279.                 }
  3280.             }
  3281.  
  3282.         if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) )
  3283.             ConductErrorDialog(anErr, cTypingCommand, ok);
  3284.             
  3285.         } // (menuResult == 0)
  3286.     
  3287.         
  3288.     return anErr;
  3289.     
  3290. } // DoKeyEvent
  3291.  
  3292. // --------------------------------------------------------------------------------------------------------------
  3293. #pragma segment Main
  3294.  
  3295. static OSErr DoAdjustCursor(WindowRef pWindow)
  3296. {
  3297.     OSErr        anErr = noErr;
  3298.     Point        mouse;
  3299.     Boolean        didAdjust = false;
  3300.     
  3301.     if (pWindow)
  3302.         {
  3303.         // not one of our windows?  don't do anything
  3304.         if (GetWindowKind(pWindow) != userKind)
  3305.             didAdjust = true;
  3306.             
  3307.         SetPort((GrafPtr) GetWindowPort(pWindow));
  3308.         
  3309.         if ( (!didAdjust) && (gMachineInfo.haveTSM) )
  3310.             {
  3311.             GetMouse(&mouse);
  3312.             LocalToGlobal(&mouse);
  3313.             if (SetTSMCursor(mouse))
  3314.                 didAdjust = true;
  3315.             }
  3316.             
  3317.         if (!didAdjust)
  3318.             {
  3319.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3320.             RgnHandle        content = NewRgn();
  3321.             Point            globalMouse;
  3322.             
  3323.             GetMouse(&mouse);
  3324.             globalMouse = mouse;
  3325.             LocalToGlobal(&globalMouse);
  3326.             
  3327.             GetWindowContentRgn(pWindow, content);
  3328.             if ((pData) && (PtInRgn(globalMouse, content)) && (PtInRect(mouse, &pData->contentRect)))
  3329.                 {
  3330.                 Rect            tempRect;
  3331.                 
  3332.                 tempRect = pData->contentRect;
  3333.                 LocalToGlobal(&TopLeft(tempRect));
  3334.                 LocalToGlobal(&BotRight(tempRect));
  3335.                 
  3336.                 if (pData->pAdjustCursor)
  3337.                     anErr = (*(pData->pAdjustCursor)) (pWindow, pData, &mouse, &tempRect);
  3338.     
  3339.                 RectRgn(gCursorRgn, &tempRect);
  3340.                 }
  3341.             DisposeRgn(content);
  3342.             }
  3343.         else
  3344.             anErr = eActionAlreadyHandled;
  3345.         }
  3346.     
  3347.     // nobody set the cursor, we do it ourselves
  3348.     if (anErr != eActionAlreadyHandled)
  3349.         SetCursor(&qd.arrow);
  3350.         
  3351.     return anErr;
  3352.     
  3353. } // DoAdjustCursor
  3354.  
  3355. // --------------------------------------------------------------------------------------------------------------
  3356. #pragma segment Main
  3357.  
  3358. static long DetermineWaitTime(WindowRef pWindow)
  3359. {
  3360.     long    waitTime = kMaxWaitTime;
  3361.     
  3362.     while (pWindow)
  3363.         {
  3364.         long            newWaitTime;
  3365.         WindowDataPtr    pData = GetWindowInfo(pWindow);
  3366.         
  3367.         if ((pData) && (pData->pCalculateIdleTime))
  3368.             newWaitTime = (*(pData->pCalculateIdleTime)) (pWindow, pData);
  3369.         else
  3370.             newWaitTime = kMaxWaitTime;
  3371.         
  3372.         if (newWaitTime < waitTime)
  3373.             waitTime = newWaitTime;
  3374.             
  3375.         pWindow = GetNextWindow(pWindow);
  3376.         }
  3377.     
  3378.     return(waitTime);
  3379.     
  3380. } // DetermineWaitTime
  3381.  
  3382. // --------------------------------------------------------------------------------------------------------------
  3383. #pragma segment Main
  3384.  
  3385. void HandleEvent(EventRecord * pEvent)
  3386. {
  3387.     WindowRef pWindow = FrontWindow();
  3388.     
  3389.     switch (pEvent->what)
  3390.         {
  3391.         case kHighLevelEvent:
  3392.             AEProcessAppleEvent(pEvent);
  3393.             break;
  3394.             
  3395.         case osEvt:
  3396.             switch ((pEvent->message >> 24) & 0xFF) /* high byte of message */
  3397.                 {        
  3398.                 case mouseMovedMessage:
  3399.                     DoAdjustCursor(pWindow);
  3400.                     break;
  3401.                     
  3402.                 case suspendResumeMessage:        /* suspend/resume is also an activate/deactivate */
  3403.                     gMachineInfo.amInBackground = (pEvent->message & 1) == 0;
  3404.                     if (pWindow)
  3405.                         DoActivate(pWindow, !gMachineInfo.amInBackground);
  3406.                         
  3407.                     break;
  3408.                 }
  3409.             break;
  3410.             
  3411.         case activateEvt:
  3412.             pWindow = (WindowRef) pEvent->message;
  3413.             DoActivate(pWindow, (pEvent->modifiers & activeFlag) != 0);
  3414.             break;
  3415.                             
  3416.         // disk inserted events must be handled, or uninitialized floppies 
  3417.         // won't be recognized.
  3418.         case diskEvt:
  3419.             if ( HiWord(pEvent->message) != noErr ) 
  3420.                 {
  3421.                 Point    where;
  3422.             
  3423.                 SetPt(&where, 70, 50);
  3424.                 ShowCursor();
  3425.                 (void) DIBadMount(where, pEvent->message);
  3426.                 }        
  3427.             break;
  3428.                 
  3429.         case mouseUp:
  3430.             break;
  3431.             
  3432.         case mouseDown:
  3433.             {
  3434.             short part = FindWindow(pEvent->where, &pWindow);                    
  3435.             
  3436.             switch ( part ) 
  3437.                 {
  3438.                 case inContent:
  3439.                     if (pWindow != FrontWindow())
  3440.                         SelectWindow(pWindow);
  3441.                     else
  3442.                         DoContentClick(pWindow);
  3443.                     break;
  3444.                     
  3445.                 case inGoAway:
  3446.                     if (TrackGoAway(pWindow, pEvent->where) )
  3447.                         DoCommand(pWindow, cClose, 0);
  3448.                     break;
  3449.                     
  3450.                 case inGrow:
  3451.                     DoGrowWindow(pWindow, pEvent);
  3452.                     break;
  3453.                     
  3454.                 case inZoomIn:
  3455.                 case inZoomOut:
  3456.                     if ( TrackBox(pWindow, pEvent->where, part) )
  3457.                         DoZoomWindow(pWindow, part);
  3458.                     break;
  3459.                     
  3460.                 case inDrag:
  3461.                     {
  3462.                     WindowDataPtr    pData = GetWindowInfo(pWindow);
  3463.                     
  3464.                     if ( (pData) && (pData->dragWindowAligned) )
  3465.                         DragAlignedWindow((WindowPtr) pWindow, pEvent->where, &qd.screenBits.bounds, nil, nil);
  3466.                     else
  3467.                         DragWindow(pWindow, pEvent->where, &qd.screenBits.bounds);
  3468.                     }
  3469.                     break;
  3470.                     
  3471.                 case inMenuBar:                /* process a mouse menu command (if any) */
  3472.                     {
  3473.                     long    menuResult;
  3474.                     
  3475.                     // force these threads to run to completion so the
  3476.                     // contents of the menus are fully initialized
  3477.                     
  3478.                     if (gFontThread != kNoThreadID)
  3479.                         {
  3480.                         gDontYield = true;
  3481.                         SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  3482.                         YieldToThread(gFontThread);
  3483.                         gDontYield = false;
  3484.                         }
  3485.                     if (gAGThread != kNoThreadID)
  3486.                         {
  3487.                         gDontYield = true;
  3488.                         SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  3489.                         YieldToThread(gAGThread);
  3490.                         gDontYield = false;
  3491.                         }
  3492.                     
  3493.                     pWindow = FrontWindow();
  3494.                     AdjustMenus(pWindow, true, false);
  3495.                     menuResult = MenuSelect(pEvent->where);
  3496.                     if ( (gMachineInfo.haveTSM) && (TSMMenuSelect(menuResult)) )
  3497.                         HiliteMenu(0);
  3498.                     else
  3499.                         DoMenuCommand(pWindow, menuResult);
  3500.                     }
  3501.                     break;
  3502.                     
  3503.                 case inSysWindow:            /* let the system handle the mouseDown */
  3504.                     SystemClick(pEvent, pWindow);
  3505.                     break;
  3506.                     
  3507.                 } // switch(part)
  3508.             }
  3509.             break;
  3510.             
  3511.         case keyDown:
  3512.         case autoKey:                        /* check for menukey equivalents */
  3513.             DoKeyEvent(pWindow, pEvent, true);
  3514.             break;
  3515.             
  3516.         case updateEvt:
  3517.             pWindow = (WindowRef) pEvent->message;
  3518.             DoUpdateWindow(pWindow);
  3519.             break;
  3520.  
  3521.         } // switch (pEvent->what)
  3522.     
  3523. } // HandleEvent
  3524.  
  3525. // --------------------------------------------------------------------------------------------------------------
  3526. #pragma segment Main
  3527.  
  3528. static OSErr    DoEventLoop(void)
  3529. {
  3530.     OSErr        anErr = noErr;
  3531.     Boolean        gotEvent;
  3532.     Boolean        trueGotEvent;
  3533.     WindowRef    pWindow;
  3534.     
  3535.     do     {
  3536.         pWindow = LMGetFirstWindow();        // walk all of our windows, even invisible ones
  3537.         
  3538.         DoAdjustCursor(pWindow);
  3539.         gotEvent = WaitNextEvent(everyEvent, &gEvent, DetermineWaitTime(pWindow), gCursorRgn);
  3540.         trueGotEvent = gotEvent;
  3541.  
  3542.         // WNE may close the window if it's owned by some silly extension.
  3543.         pWindow = LMGetFirstWindow();        
  3544.         
  3545.         // let text services handle the event first if it wishes to do so
  3546.         if ( gMachineInfo.haveTSM )
  3547.             {
  3548.             ScriptCode    keyboardScript;
  3549.             WindowRef    theFront = FrontWindow();
  3550.             
  3551.             if (theFront)
  3552.                 {
  3553.                 SetPort((GrafPtr) GetWindowPort(theFront));
  3554.                 
  3555.                 keyboardScript = GetScriptManagerVariable(smKeyScript);
  3556.                 if (FontToScript(qd.thePort->txFont) != keyboardScript)
  3557.                     TextFont(GetScriptVariable(keyboardScript, smScriptAppFond));
  3558.                 }
  3559.             
  3560.             if (TSMEvent(&gEvent))
  3561.                 gotEvent = false;
  3562.             }
  3563.             
  3564.         // let all windows filter this event, and get time if they wish to
  3565.         while (pWindow)
  3566.             {
  3567.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  3568.             Boolean            finishedEvent = false;
  3569.             
  3570.             // help manager for the front window
  3571.             if ( (pWindow == FrontWindow()) && (pData) && (!gMachineInfo.amInBackground) && (HMGetBalloons()) )
  3572.                 {
  3573.                 Point    theMouse, tipLocation;
  3574.                 short    newBalloon = iNoBalloon;
  3575.                 Rect    tempRect;
  3576.                 
  3577.                 // find out where the mouse is                
  3578.                 SetPort((GrafPtr) GetWindowPort(pWindow));
  3579.                 GetMouse(&theMouse);
  3580.                 
  3581.                 // and only do something if we are within the window itself
  3582.                 if (PtInRect(theMouse, &GetWindowPort(pWindow)->portRect))
  3583.                     {
  3584.                     // is it in the vertical scroll bar?
  3585.                     if (pData->vScroll)
  3586.                         {
  3587.                         tempRect = (**(pData->vScroll)).contrlRect;
  3588.                         if (PtInRect(theMouse, &tempRect))
  3589.                             {
  3590.                             newBalloon = iHelpActiveScroll;
  3591.                             if (GetControlMinimum(pData->vScroll) == GetControlMaximum(pData->vScroll))
  3592.                                 newBalloon = iHelpDimVertScroll;
  3593.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  3594.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  3595.                             }
  3596.                         }
  3597.                         
  3598.                     // is it in the horizontal scroll bar?
  3599.                     if (pData->hScroll)
  3600.                         {
  3601.                         tempRect = (**(pData->hScroll)).contrlRect;
  3602.                         if (PtInRect(theMouse, &tempRect))
  3603.                             {
  3604.                             newBalloon = iHelpActiveScroll;
  3605.                             if (GetControlMinimum(pData->hScroll) == GetControlMaximum(pData->hScroll))
  3606.                                 newBalloon = iHelpDimHorizScroll;
  3607.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  3608.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  3609.                             }
  3610.                         }
  3611.                     
  3612.                     // is it in the grow box?
  3613.                     if (pData->hasGrow)
  3614.                         {
  3615.                         CalculateGrowIcon(pData, &tempRect);
  3616.                         if (PtInRect(theMouse, &tempRect))
  3617.                             {
  3618.                             newBalloon = iHelpGrowBox;
  3619.                             tipLocation.h = tempRect.right - kFromBottomTipOffset;
  3620.                             tipLocation.v = tempRect.bottom - kFromBottomTipOffset;
  3621.                             }
  3622.                         }
  3623.                     
  3624.                     // none of the above, must be the content
  3625.                     if (newBalloon == iNoBalloon)
  3626.                         {
  3627.                         newBalloon = iHelpGenericContent;
  3628.                         tempRect = pData->contentRect;
  3629.                         if (pData->pGetBalloon)
  3630.                             (*(pData->pGetBalloon)) (pWindow, pData, &theMouse, &newBalloon, &tempRect);
  3631.                             
  3632.                         tipLocation.h = tempRect.left + kFromTopTipOffset;
  3633.                         tipLocation.v = tempRect.top + kFromTopTipOffset;
  3634.                         }
  3635.                         
  3636.                     // show our new balloon, or remove the old one
  3637.                     if (newBalloon != iNoBalloon)
  3638.                         {
  3639.                         if ( (gMachineInfo.lastBalloonIndex != newBalloon) || (!HMIsBalloon()) )
  3640.                             {
  3641.                             HMMessageRecord    message;
  3642.                             
  3643.                             if (newBalloon != iDidTheBalloon)
  3644.                                 {
  3645.                                 message.hmmHelpType = khmmString;
  3646.                                 GetIndString(message.u.hmmString, kWindowHelpID, newBalloon);
  3647.                                 LocalToGlobal(&tipLocation);
  3648.                                 (void) HMShowBalloon(&message, tipLocation, nil, nil, 0, kDefaultBalloonVariant, 0);
  3649.                                 }
  3650.                             gMachineInfo.lastBalloonIndex = newBalloon;
  3651.                             }
  3652.                         }
  3653.                     else
  3654.                         HMRemoveBalloon();
  3655.                     }
  3656.                     
  3657.                     
  3658.                 }
  3659.                 
  3660.             // if we hit a window we know about, then do filtering
  3661.             if (pData)
  3662.                 {
  3663.                 if (pData->pFilterEvent)
  3664.                     finishedEvent = (*(pData->pFilterEvent)) (pWindow, pData, &gEvent);
  3665.                 }
  3666.  
  3667.             // if filtering indicates complete handling of event, then stop, and
  3668.             // do no regular processing.
  3669.             if (finishedEvent)
  3670.                 {
  3671.                 gotEvent = false;
  3672.                 pWindow = nil;
  3673.                 }
  3674.             else
  3675.                 pWindow = GetNextWindow(pWindow);
  3676.             }
  3677.             
  3678.         if (gotEvent)
  3679.             HandleEvent(&gEvent);
  3680.             
  3681.         // close request?
  3682.         if (gAllDone)
  3683.             {
  3684.             pWindow = FrontWindow();
  3685.             while ((gAllDone) && (pWindow) )
  3686.                 {
  3687.                 WindowRef    nextWindow = GetNextWindow(pWindow);
  3688.                 OSErr        closeError = DoCloseWindow(pWindow);
  3689.                 
  3690.                 // window didn't close?  then don't quit
  3691.                 if (pWindow == FrontWindow())
  3692.                     gAllDone = false;
  3693.                     
  3694.                 // something bad happened, then don't quit
  3695.                 if ( (closeError != noErr) /* && (closeError != eUserCanceled) */ )
  3696.                     gAllDone = false;
  3697.                     
  3698.                 pWindow = nextWindow;
  3699.                 }
  3700.             }
  3701.         
  3702.         // our threads are low-priority, so we only give time to them on idle
  3703.         if (gMachineInfo.haveThreads && !trueGotEvent && !gAllDone)
  3704.             YieldToAnyThread();
  3705.         
  3706.         } while (!gAllDone);
  3707.         
  3708.     return anErr;
  3709.     
  3710. } // DoEventLoop
  3711.  
  3712.  
  3713. // --------------------------------------------------------------------------------------------------------------
  3714. // DRAG MANAGEMENT GLOBAL SUPPORT ROUTINES
  3715. // --------------------------------------------------------------------------------------------------------------
  3716.  
  3717. // Globals for our drag handlers
  3718.  
  3719. Boolean                gCanAccept;                // if we can receive the item(s) being dragged
  3720.  
  3721. // --------------------------------------------------------------------------------------------------------------
  3722. #pragma segment Drag
  3723.  
  3724. static pascal OSErr GlobalTrackingHandler(short message, WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  3725. {
  3726.     #pragma unused(handlerRefCon)
  3727.  
  3728.     WindowDataPtr pData = GetWindowInfo(pWindow);
  3729.  
  3730.     // Call the tracking handler associated with this type of window. Only allow messages referencing
  3731.     // a specific window to be passed to the handler.
  3732.  
  3733.     if (pData)
  3734.         {    
  3735.         if (pData->pDragTracking)
  3736.             return ((*(pData->pDragTracking)) (pWindow, pData, theDragRef, message));
  3737.         }
  3738.     
  3739.     return noErr;
  3740.  
  3741. } // GlobalTrackingHandler
  3742.  
  3743. DragTrackingHandlerUPP gGlobalTrackingHandler;
  3744.  
  3745. // --------------------------------------------------------------------------------------------------------------
  3746. #pragma segment Drag
  3747.  
  3748. static pascal OSErr GlobalReceiveHandler(WindowRef pWindow, void *handlerRefCon, DragReference theDragRef)
  3749. {
  3750.     #pragma unused(handlerRefCon)
  3751.  
  3752.     WindowDataPtr pData = GetWindowInfo(pWindow);
  3753.     
  3754.     if (pData)
  3755.         {
  3756.         if (pData->pDragTracking)
  3757.             return ((*(pData->pDragReceive)) (pWindow, pData, theDragRef));
  3758.         }
  3759.  
  3760.     return noErr;
  3761.  
  3762. } // GlobalReceiveHandler
  3763.  
  3764. DragReceiveHandlerUPP gGlobalReceiveHandler;
  3765.  
  3766. // --------------------------------------------------------------------------------------------------------------
  3767. //
  3768. // IsOnlyThisFlavor - Given a DragReference and a FlavorType, we iterate through the drag items to determine if
  3769. //                      all are of flavor theType. If this is so, we return true. If any of the items are not
  3770. //                      theType, we return false, indicating that we should not accept the drag.
  3771. //
  3772. #pragma segment Drag
  3773.  
  3774. Boolean IsOnlyThisFlavor(DragReference theDragRef, FlavorType theType)
  3775. {
  3776.     unsigned short    items, index;
  3777.     FlavorFlags        theFlags;
  3778.     ItemReference    itemID;
  3779.     OSErr            anErr = noErr;
  3780.  
  3781.     CountDragItems(theDragRef, &items);
  3782.     
  3783.     for(index = 1; index <= items; index++)
  3784.         {
  3785.         GetDragItemReferenceNumber(theDragRef, index, &itemID);
  3786.  
  3787.         anErr = GetFlavorFlags(theDragRef, itemID, theType, &theFlags);
  3788.         if(anErr == noErr)
  3789.             continue;    // it's okay, this flavor is cool
  3790.  
  3791.         return false;    // this item has at least one flavor we don't like
  3792.         }
  3793.  
  3794.     return true;        // all flavors in this item were cool
  3795.  
  3796. } // IsOnlyThisFlavor
  3797.  
  3798. // --------------------------------------------------------------------------------------------------------------
  3799. //
  3800. // IsDropInFinderTrash - Returns true if the given dropLocation AEDesc is a descriptor of the Finder's Trash.
  3801. //
  3802. #pragma segment Drag
  3803.  
  3804. Boolean IsDropInFinderTrash(AEDesc *dropLocation)
  3805. {
  3806.     OSErr            result;
  3807.     AEDesc            dropSpec;
  3808.     FSSpec            *theSpec;
  3809.     CInfoPBRec        thePB;
  3810.     short            trashVRefNum;
  3811.     long            trashDirID;
  3812.  
  3813.     //    Coerce the dropLocation descriptor into an FSSpec. If there's no dropLocation or
  3814.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  3815.  
  3816.     if ((dropLocation->descriptorType != typeNull) &&
  3817.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr)) 
  3818.         {
  3819.         unsigned char flags = HGetState(dropSpec.dataHandle);
  3820.         
  3821.         HLock(dropSpec.dataHandle);
  3822.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  3823.  
  3824.         //    Get the directory ID of the given dropLocation object.
  3825.  
  3826.         thePB.dirInfo.ioCompletion = 0L;
  3827.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  3828.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  3829.         thePB.dirInfo.ioFDirIndex = 0;
  3830.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  3831.  
  3832.         result = PBGetCatInfoSync(&thePB);
  3833.  
  3834.         HSetState(dropSpec.dataHandle, flags);
  3835.         AEDisposeDesc(&dropSpec);
  3836.  
  3837.         if (result != noErr)
  3838.             return false;
  3839.  
  3840.         //    If the result is not a directory, it must not be the Trash.
  3841.  
  3842.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  3843.             return false;
  3844.  
  3845.         //    Get information about the Trash folder.
  3846.  
  3847.         FindFolder(theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  3848.  
  3849.         //    If the directory ID of the dropLocation object is the same as the directory ID
  3850.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  3851.  
  3852.         if (thePB.dirInfo.ioDrDirID == trashDirID)
  3853.             return true;
  3854.         }
  3855.  
  3856.     return false;
  3857.  
  3858. } // IsDropInFinderTrash
  3859.  
  3860. // --------------------------------------------------------------------------------------------------------------
  3861. // APPLE EVENT SUPPORT ROUTINES
  3862. // --------------------------------------------------------------------------------------------------------------
  3863. #pragma segment Main
  3864.  
  3865. static OSErr    MissingParameterCheck(
  3866.     AppleEvent     *inputEvent)
  3867. /*
  3868.     This routine checks an input AppleEvent for the missing keyword.
  3869.     If the missing keyword is found, that means that some required
  3870.     parameters were missing (ie, an error). 
  3871.     
  3872.     However, if the missing keyword isn't found, that means that we aren't missing 
  3873.     any required parameters (that is to say, all REQUIRED parameters were supplied
  3874.     by the person who created the event).
  3875.     
  3876.     SOME DAY, THE ABOVE COMMENT WILL MAKE SENSE TO YOU.  IT STILL DOESN'T
  3877.     TO ME AND I WAS THE ONE WHO WROTE IT.
  3878. */
  3879. {
  3880.     OSErr        anErr;
  3881.     AEKeyword    missingKeyword;
  3882.     DescType    ignoredActualType;
  3883.     Size        ignoredActualSize;
  3884.     
  3885.     anErr = AEGetAttributePtr(
  3886.         inputEvent, 
  3887.         keyMissedKeywordAttr,
  3888.         typeWildCard,
  3889.         &ignoredActualType,
  3890.         (Ptr) &missingKeyword,
  3891.         sizeof(AEKeyword),
  3892.         &ignoredActualSize);
  3893.             
  3894.     if (anErr == noErr)
  3895.         anErr = errAEParamMissed;
  3896.     else
  3897.         if (anErr == errAEDescNotFound)
  3898.             anErr = noErr;
  3899.         
  3900.     return anErr;
  3901.     
  3902. } // MissingParameterCheck
  3903.  
  3904. // --------------------------------------------------------------------------------------------------------------
  3905. // Globals for our handlers
  3906. Boolean gQuitAfterPrint = true;
  3907.  
  3908. // --------------------------------------------------------------------------------------------------------------
  3909. #pragma segment Main
  3910.  
  3911. static pascal OSErr    DoOpenApp(
  3912.     AppleEvent     *inputEvent,
  3913.     AppleEvent     *outputEvent,
  3914.     long        handlerRefCon)
  3915. {
  3916. #pragma unused (outputEvent, handlerRefCon)
  3917.  
  3918.     DoCommand(nil, cNew, 0);
  3919.     gQuitAfterPrint = false;
  3920.     
  3921.     // so that the initial document opens more quickly, we don't start
  3922.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  3923.     if (gStarterThread != kNoThreadID)
  3924.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  3925.     
  3926.     return(MissingParameterCheck(inputEvent));
  3927.     
  3928. } // DoAppOpen
  3929.  
  3930. // --------------------------------------------------------------------------------------------------------------
  3931. #pragma segment Main
  3932.  
  3933. static pascal OSErr    DoQuitApp(
  3934.     AppleEvent     *inputEvent,
  3935.     AppleEvent     *outputEvent,
  3936.     long        handlerRefCon)
  3937. {
  3938. #pragma unused (outputEvent, handlerRefCon)
  3939.  
  3940.     DoCommand(nil, cQuit, 0);
  3941.  
  3942.     return(MissingParameterCheck(inputEvent));
  3943.     
  3944. } // DoQuitApp
  3945.  
  3946. // --------------------------------------------------------------------------------------------------------------
  3947. #pragma segment Main
  3948.  
  3949. static pascal OSErr    DoOpenOrPrint(
  3950.     AppleEvent     *inputEvent,
  3951.     StringPtr    pPrinterName)    // nil => do not print, zero length => print to default, other => printer name
  3952. {
  3953.  
  3954.     OSErr        anErr, anErr2;
  3955.     AEDescList    docList;                // list of docs passed in
  3956.     long        index, itemsInList;
  3957.     void*        hPrint;
  3958.     Boolean        wasAlreadyOpen;
  3959.     
  3960.     anErr = AEGetParamDesc( inputEvent, keyDirectObject, typeAEList, &docList);
  3961.     nrequire(anErr, GetFileList);
  3962.  
  3963.     anErr = AECountItems( &docList, &itemsInList);            // how many files passed in
  3964.     nrequire(anErr, CountDocs);
  3965.     for (index = 1; index <= itemsInList; index++)            // handle each file passed in
  3966.         {    
  3967.         AEKeyword    keywd;
  3968.         DescType    returnedType;
  3969.         Size        actualSize;
  3970.         FSSpec        theFSS;    
  3971.  
  3972.         anErr = AEGetNthPtr( &docList, index, typeFSS, &keywd, &returnedType,    // get file's info
  3973.                             (Ptr)(&theFSS), sizeof(theFSS), &actualSize);
  3974.         nrequire(anErr, AEGetNthPtr);
  3975.         
  3976.         {
  3977.         FInfo    theFileInfo;
  3978.         
  3979.         anErr = FSpGetFInfo(&theFSS, &theFileInfo);
  3980.         if (anErr == noErr)
  3981.             anErr = DetermineWindowTypeOrOpen(&theFSS, theFileInfo.fdType, nil, nil, &wasAlreadyOpen);
  3982.             
  3983.         if (anErr == eDocumentWrongKind)
  3984.             {
  3985.             if (pPrinterName)
  3986.                 ConductErrorDialog(anErr, cPrint, cancel);
  3987.             else
  3988.                 ConductErrorDialog(anErr, cOpen, cancel);
  3989.  
  3990.             anErr = noErr;
  3991.             break;
  3992.             }
  3993.             
  3994.         nrequire(anErr, DetermineWindowTypeOrOpen);
  3995.         }
  3996.         
  3997.         if (pPrinterName)
  3998.             {
  3999.             WindowRef        pWindow = FrontWindow();
  4000.             WindowDataPtr    pData = GetWindowInfo(pWindow);
  4001.             
  4002.             if (pData->pPrintPage)
  4003.                 {
  4004.                 if (index == 1)
  4005.                     {
  4006.                     THPrint    aePrintRecord = NULL;
  4007.                     Boolean    showDialog = true;    // default to true
  4008.  
  4009.                     // Get the default print record into pData->hPrint
  4010.                     anErr = DoDefault(pData);
  4011.                     if (anErr == noErr) {
  4012.  
  4013.                         // look for the [optional] print settings parameter
  4014.                         anErr = getPrintRecordFromEvent(inputEvent, &aePrintRecord);
  4015.                         if ((anErr == noErr) && (aePrintRecord != NULL))
  4016.                             {
  4017.                             THPrint    jobPrintRecord = NULL;
  4018.                             // There was a print settings param. It's been saved in
  4019.                             // aePrintRecord.
  4020.  
  4021.                             // Merge the record we retrieved with a default print record.
  4022.                             // This is necessary so that some of the hints, specifically
  4023.                             // first and last page, get merged into the print record.
  4024.                             
  4025.                             // Since SimpleText hasn't opened the driver at this point,
  4026.                             // PrOpen and PrClose must wrap the call to PrJobMerge().
  4027.                             PrOpen();
  4028.                             anErr = getPrintJobPrintRec(pData->hPrint, aePrintRecord, &jobPrintRecord);
  4029.                             if (anErr == noErr) {
  4030.                                 pData->hPrint = jobPrintRecord;
  4031.                             }
  4032.                             PrClose();
  4033.                             
  4034.                             // pData->hPrint now has a valid print record which can either
  4035.                             // be used directly or can be used to "seed" the print dialog
  4036.                             }
  4037.  
  4038.                         if (anErr == noErr)
  4039.                             {
  4040.                             // look for the [optional] parameter saying whether we show the print dialog
  4041.                             anErr = getPrintJobShowDialog(inputEvent, &showDialog);
  4042.                             if (anErr == noErr)
  4043.                                 {
  4044.                                 if (!showDialog)
  4045.                                     {
  4046.                                     // no need to show the print dialog, since we were told not to.
  4047.                                     // We do need to copy the print record from the window into
  4048.                                     // hPrint so it can be passed into doPrint().
  4049.                                     hPrint = pData->hPrint;
  4050.                                     }
  4051.                                 else
  4052.                                     {
  4053.                                     // we were told to show the print dialog. pWindow contains
  4054.                                     // (in pData->hPrint) the print record that should set up
  4055.                                     // the dialog. I want to call my new version of DoPrintSetup
  4056.                                     // which does not default the print record that has been
  4057.                                     // passed in.
  4058.                                     
  4059.                                     // NOTE: First Page and Last Page will not be entered into
  4060.                                     // the dialog correctly and the dialog will show "All Pages".
  4061.                                     // This is a bug in the driver (LaserWriter 8, version 8.7)
  4062.                                     // and will be fixed in a future version of the driver.
  4063.                                     anErr = DoPrintSetupNoDefault(pWindow, pPrinterName);
  4064.                                     if (anErr == noErr)
  4065.                                         hPrint = pData->hPrint;
  4066.                                     }
  4067.                                 }
  4068.                             }
  4069.                         }
  4070.                     }
  4071.                     
  4072.                 if (anErr == noErr)
  4073.                     anErr = DoPrint(pWindow, hPrint, false);
  4074.                     
  4075.                 if (index != itemsInList)
  4076.                     pData->hPrint = nil;
  4077.                 }
  4078.             
  4079.             if (!wasAlreadyOpen)
  4080.                 DoCloseWindow(pWindow);
  4081.  
  4082.             if (anErr != noErr)
  4083.                 break;
  4084.             }
  4085.         }
  4086.  
  4087.     // finally, make sure we didn't miss any parameters
  4088.     anErr2 = MissingParameterCheck(inputEvent);
  4089.     if (anErr == noErr)
  4090.         anErr = anErr2;
  4091.         
  4092. // FALL THROUGH EXCEPTION HANDLING
  4093. DetermineWindowTypeOrOpen:
  4094. AEGetNthPtr:
  4095. CountDocs:
  4096.     // done with doc list
  4097.     (void) AEDisposeDesc( &docList);                        
  4098.     
  4099. GetFileList:
  4100.  
  4101.     // don't report cancels from prints
  4102.     if (pPrinterName)
  4103.         {
  4104.         if (anErr == iPrAbort)
  4105.             anErr = noErr;
  4106.         }
  4107.     
  4108.     if ( (anErr != noErr) && (anErr != eActionAlreadyHandled) && (anErr != eUserCanceled) )
  4109.         {
  4110.         if (pPrinterName)
  4111.             ConductErrorDialog(anErr, cPrint, cancel);
  4112.         else
  4113.             ConductErrorDialog(anErr, cOpen, cancel);
  4114.         }
  4115.         
  4116.     return anErr;
  4117.     
  4118. } // DoOpenOrPrint
  4119.  
  4120. // --------------------------------------------------------------------------------------------------------------
  4121. #pragma segment Main
  4122.  
  4123. static pascal OSErr    DoOpenDocument(
  4124.     AppleEvent     *inputEvent,
  4125.     AppleEvent     *outputEvent,
  4126.     long        handlerRefCon)
  4127. {
  4128. #pragma unused (outputEvent, handlerRefCon)
  4129.  
  4130.     OSErr        anErr;
  4131.     
  4132.     if (IsCommandEnabled(cOpen))
  4133.         {
  4134.         gQuitAfterPrint = false;
  4135.         anErr = DoOpenOrPrint(inputEvent, nil);
  4136.         }
  4137.     else
  4138.         {
  4139.         anErr = errAEEventNotHandled;
  4140.         ConductErrorDialog(anErr, cOpen, cancel);
  4141.         }
  4142.         
  4143.     // so that the initial document opens more quickly, we don't start
  4144.     // the threads until we get an OpenApp or OpenDocument AppleEvent
  4145.     if (gStarterThread != kNoThreadID)
  4146.         SetThreadState(gStarterThread, kReadyThreadState, gStarterThread);
  4147.     
  4148.     return anErr;
  4149.     
  4150. } // DoOpenDocument
  4151.  
  4152. // --------------------------------------------------------------------------------------------------------------
  4153. #pragma segment Main
  4154.  
  4155. static pascal OSErr    DoPrintDocument(
  4156.     AppleEvent     *inputEvent,
  4157.     AppleEvent     *outputEvent,
  4158.     long        handlerRefCon)
  4159. {
  4160. #pragma unused (outputEvent, handlerRefCon)
  4161.     OSErr        anErr;
  4162.     FSSpec        printerFSS;
  4163.     AEDescList    dtpList;                // list of docs passed in
  4164.     
  4165.     if (IsCommandEnabled(cOpen))
  4166.         {
  4167.         // try to find out if this doc was dropped onto a printer
  4168.         anErr = AEGetAttributeDesc( inputEvent, keyOptionalKeywordAttr, typeAEList, &dtpList);
  4169.     
  4170.         if (anErr == noErr)                                            // doc dragged to dtp?
  4171.             {
  4172.             AEKeyword    keywd;
  4173.             DescType    returnedType;
  4174.             Size        actualSize;
  4175.     
  4176.             anErr = AEGetNthPtr( &dtpList, 1, typeFSS, &keywd, &returnedType,    // get dtp info
  4177.                             (Ptr)(&printerFSS), sizeof(printerFSS), &actualSize);
  4178.             }
  4179.             
  4180.         // if it wasn't, that's not an error, just print normally
  4181.         if (anErr != noErr)
  4182.             {
  4183.             printerFSS.name[0] = 0;
  4184.             anErr = noErr;
  4185.             }
  4186.             
  4187.         anErr = DoOpenOrPrint(inputEvent, &printerFSS.name[0]);
  4188.         
  4189.         // if we are opened just for printing -- quit afterwards
  4190.         if (gQuitAfterPrint)
  4191.             DoCommand(nil, cQuit, 0);
  4192.         }
  4193.     else
  4194.         {
  4195.         anErr = errAEEventNotHandled;
  4196.         ConductErrorDialog(anErr, cPrint, cancel);
  4197.         }
  4198.         
  4199.     return anErr;
  4200.     
  4201. } // DoPrintDocument
  4202.  
  4203. #if GENERATINGCFM
  4204.     static RoutineDescriptor    gDoOpenAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenApp);
  4205.     static AEEventHandlerUPP    gDoOpenApp = &gDoOpenAppRD;
  4206.  
  4207.     static RoutineDescriptor    gDoQuitAppRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoQuitApp);
  4208.     static AEEventHandlerUPP    gDoQuitApp = &gDoQuitAppRD;
  4209.  
  4210.     static RoutineDescriptor    gDoOpenDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoOpenDocument);
  4211.     static AEEventHandlerUPP    gDoOpenDocument = &gDoOpenDocumentRD;
  4212.  
  4213.     static RoutineDescriptor    gDoPrintDocumentRD = BUILD_ROUTINE_DESCRIPTOR(uppAEEventHandlerProcInfo, DoPrintDocument);
  4214.     static AEEventHandlerUPP    gDoPrintDocument = &gDoPrintDocumentRD;
  4215. #else
  4216.     static AEEventHandlerUPP    gDoOpenApp = (AEEventHandlerUPP) DoOpenApp;
  4217.     static AEEventHandlerUPP    gDoQuitApp = (AEEventHandlerUPP) DoQuitApp;
  4218.     static AEEventHandlerUPP    gDoOpenDocument = (AEEventHandlerUPP) DoOpenDocument;
  4219.     static AEEventHandlerUPP    gDoPrintDocument = (AEEventHandlerUPP) DoPrintDocument;
  4220. #endif
  4221. // --------------------------------------------------------------------------------------------------------------
  4222. #pragma segment Main
  4223.  
  4224. static pascal OSErr SimpleTextCoachHandler(Rect *pRect, Ptr name, long refCon)
  4225. {
  4226. #pragma unused (refCon)
  4227.  
  4228.     OSErr            anErr = noErr;
  4229.     WindowRef        pWindow = FrontWindow();
  4230.     WindowDataPtr     pData = GetWindowInfo(pWindow);
  4231.     
  4232.     if ((pData) && (pData->pGetCoachRectangle))
  4233.         anErr = (*(pData->pGetCoachRectangle)) (pWindow, pData, pRect, name);
  4234.         
  4235.     return(anErr);
  4236.     
  4237. } // SimpleTextCoachHandler
  4238.  
  4239. // --------------------------------------------------------------------------------------------------------------
  4240. // MAIN INITIALIZE/SHUTDOWN/LOOP ROUTINES
  4241. // --------------------------------------------------------------------------------------------------------------
  4242. #pragma segment Main
  4243.  
  4244. static pascal void* StarterThread(void* threadParam)
  4245. {
  4246.     #pragma unused(threadParam)
  4247.     
  4248.     /*
  4249.         All threads, including the starter thread, are initially created
  4250.         in a suspended state. The starter thread is made ready to run when
  4251.         we get an open application or open document event. It runs until
  4252.         there are no activate or update events pending, and then starts the
  4253.         font menu and Apple Guide threads. This gives much better performance
  4254.         for the initial creation and update of a document, because the threads
  4255.         (especially the font thread) chew up a lot of time at first.
  4256.         
  4257.         The starter thread isn't really necessary - we could get the same
  4258.         effect by just checking in the event loop for activate/update events -
  4259.         but hey, it's a lot easier to do it this way, doesn't cost much, and
  4260.         isn't that what threads are for?
  4261.     */
  4262.     
  4263.     for (;;)
  4264.         {
  4265.         EventRecord        er;
  4266.     
  4267.         YieldToAnyThread();
  4268.         
  4269.         if (!EventAvail(activMask | updateMask, &er))
  4270.             {
  4271.             if (gFontThread != kNoThreadID)
  4272.                 SetThreadState(gFontThread, kReadyThreadState, gFontThread);
  4273.             if (gAGThread != kNoThreadID)
  4274.                 SetThreadState(gAGThread, kReadyThreadState, gAGThread);
  4275.             
  4276.             break;
  4277.             }
  4278.         }
  4279.     
  4280.     gStarterThread = kNoThreadID;
  4281.     return NULL;
  4282.     
  4283. } // StarterThread
  4284.  
  4285. // --------------------------------------------------------------------------------------------------------------
  4286. #pragma segment Initialize
  4287.  
  4288. static OSErr CreateThread(ThreadEntryProcPtr pThread, void* threadParam, ThreadID* ptid)
  4289. {
  4290.     OSErr    anErr;
  4291.     
  4292.     anErr = NewThread(kCooperativeThread, pThread, threadParam, 0, kNewSuspend,
  4293.         &gThreadResults, ptid);
  4294.         
  4295.     if (anErr == noErr && gStarterThread == kNoThreadID)
  4296.         {
  4297.         anErr = NewThread(kCooperativeThread, StarterThread, NULL, 0, kNewSuspend,
  4298.             &gThreadResults, &gStarterThread);
  4299.         if (anErr != noErr)
  4300.             DisposeThread(*ptid, &gThreadResults, false);
  4301.             // anErr remains != noErr
  4302.         }
  4303.         
  4304.     return anErr;
  4305. }
  4306.         
  4307. // --------------------------------------------------------------------------------------------------------------
  4308. #pragma segment Initialize
  4309.  
  4310. static OSErr BuildFontMenu(MenuHandle menu)
  4311. {
  4312.     OSErr    anErr = noErr;
  4313.     
  4314.     AppendResMenu(menu, 'FONT');
  4315.     
  4316.     return(anErr);
  4317.     
  4318. } // BuildFontMenu
  4319.  
  4320. // --------------------------------------------------------------------------------------------------------------
  4321. #pragma segment Initialize
  4322.  
  4323. static OSErr    DoInitialize(void)
  4324. {
  4325.     short                count;            // loop counter
  4326.     Handle                menuBar;        // for loading our menus in
  4327.     OSErr                anErr = noErr;    // any errors we get, none so far
  4328.     long                version;        // version for Gestalt calls
  4329.     
  4330.     InitGraf((Ptr) &qd.thePort);
  4331.     InitFonts();
  4332.     InitWindows();
  4333.     InitMenus();
  4334.     TEInit();
  4335.     InitDialogs(nil);
  4336.     InitCursor();
  4337.     
  4338.     gAllDone = false;
  4339.     
  4340.     // check that the system is correct to handle things
  4341.     SysEnvirons(1, &gMachineInfo.theEnvirons);
  4342.     if (gMachineInfo.theEnvirons.systemVersion < 0x0700)
  4343.         {
  4344.         // Wait for app to come to front
  4345.         for (count = 1; count <= 3; ++count)
  4346.             EventAvail(everyEvent, &gEvent);
  4347.             
  4348.         anErr = eMachineToOld;
  4349.         nrequire(anErr, SysEnvirons);
  4350.         }
  4351.  
  4352.     gMachineInfo.lastBalloonIndex = iNoBalloon;
  4353.     gMachineInfo.amInBackground = false;
  4354.     gMachineInfo.documentCount  = 1;
  4355.     gMachineInfo.haveQuickTime     = (Gestalt(gestaltQuickTime, &version) == noErr);
  4356.     gMachineInfo.haveRecording     = (Gestalt(gestaltSoundAttr, &version) == noErr) && ((version & (1<<gestaltHasSoundInputDevice)) != 0);
  4357.     gMachineInfo.haveTTS         = (Gestalt(gestaltSpeechAttr, &version) == noErr) && ((version & (1<<gestaltSpeechMgrPresent)) != 0);
  4358.     gMachineInfo.haveTSM         = (Gestalt(gestaltTSMgrVersion, &version) == noErr) && (version >= 1);
  4359.     gMachineInfo.haveTSMTE         = (Gestalt(gestaltTSMTEAttr, &version) == noErr) && ((version & (1<<gestaltTSMTE)) != 0);
  4360.     gMachineInfo.haveDragMgr    = (Gestalt(gestaltDragMgrAttr, &version) == noErr) && ((version & (1<<gestaltDragMgrPresent)) != 0) &&
  4361.                                     (Gestalt(gestaltTEAttr, &version) == noErr) && ((version & (1<<gestaltTEHasGetHiliteRgn)) != 0);
  4362.     gMachineInfo.haveThreeD        = false;
  4363.     gMachineInfo.haveThreads    = (Gestalt(gestaltThreadMgrAttr, &version) == noErr) && ((version & (1<<gestaltThreadMgrPresent)) != 0);
  4364.     
  4365.     #if GENERATINGPOWERPC
  4366.         {
  4367.         CFragConnectionID    connID;
  4368.         Ptr                 mainAddr;
  4369.         Str255                errName;
  4370.         
  4371.         if ( (gMachineInfo.haveQuickTime)     && (GetSharedLibrary("\pQuickTimeLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  4372.             gMachineInfo.haveQuickTime = false;
  4373.         if ( (gMachineInfo.haveTTS)         && (GetSharedLibrary("\pSpeechLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  4374.             gMachineInfo.haveTTS = false;
  4375.         if ( (gMachineInfo.haveDragMgr)        && (GetSharedLibrary("\pDragLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  4376.             gMachineInfo.haveDragMgr = false;
  4377.         if ( (gMachineInfo.haveThreads)     && (GetSharedLibrary("\pThreadsLib", kPowerPCCFragArch, kFindCFrag, &connID, &mainAddr, errName) != noErr) )
  4378.             gMachineInfo.haveThreads = false;
  4379.         }
  4380.     #endif
  4381.     
  4382.  
  4383.     // initialize text services if they exist
  4384.     if (gMachineInfo.haveTSMTE)
  4385.         {
  4386.         if (InitTSMAwareApplication() != noErr)
  4387.             {
  4388.             gMachineInfo.haveTSM = false;
  4389.             gMachineInfo.haveTSMTE = false;
  4390.             }
  4391.         }
  4392.         
  4393.     // save away info we need from the get-go    
  4394.     gApplicationResFile = CurResFile();
  4395.     gCursorRgn = NewRgn();
  4396.  
  4397.     // load up the menus
  4398.     menuBar = (Handle) GetNewMBar(rMenuBar);            /* read menus into menu bar */
  4399.     anErr = ResError();
  4400.     if ( (anErr == noErr) && (menuBar == nil) )
  4401.         anErr = resNotFound;
  4402.     nrequire(anErr, GetNewMBar);
  4403.     
  4404.     // install menus
  4405.     SetMenuBar(menuBar);    
  4406.     DisposeHandle(menuBar);
  4407.  
  4408.     // build the Apple menu
  4409.     AppendResMenu(GetMenuHandle(mApple), 'DRVR');    /* add DA names to Apple menu */
  4410.     
  4411.     // Build the font menu
  4412.     anErr = BuildFontMenu(GetMenuHandle(mFont));
  4413.     nrequire(anErr, BuildFontMenu);
  4414.     
  4415.     // insert our heirarchical menus
  4416.     {
  4417.     MenuHandle     menu = GetMenu( mVoices );
  4418.     short        menuID, itemID;
  4419.     
  4420.     InsertMenu( menu, hierMenu );
  4421.     
  4422.     CommandToIDs(cSelectVoice, &menuID, &itemID);
  4423.     menu = GetMenuHandle(menuID);
  4424.  
  4425.     SetItemCmd( menu, itemID, hMenuCmd );
  4426.     SetItemMark( menu, itemID, mVoices );
  4427.     }
  4428.  
  4429.     AdjustMenus(nil, true, false);
  4430.     DrawMenuBar();
  4431.     
  4432.     // start up QuickTime, but problems result in us pretending not to have it
  4433.     if (gMachineInfo.haveQuickTime)
  4434.         if (EnterMovies() != noErr)
  4435.             gMachineInfo.haveQuickTime = false;
  4436.         
  4437.     // Install AppleEvent handlers for the base classes
  4438.  
  4439.     #define INSTALL(event, handler) \
  4440.             AEInstallEventHandler(kCoreEventClass, event, handler, 0, false)
  4441.  
  4442.     INSTALL (kAEOpenApplication, gDoOpenApp);
  4443.     INSTALL (kAEQuitApplication, gDoQuitApp);
  4444.     INSTALL (kAEOpenDocuments,   gDoOpenDocument);
  4445.     INSTALL (kAEPrintDocuments,  gDoPrintDocument);
  4446.  
  4447.     #undef INSTALL
  4448.  
  4449.     // Install our global dragging procs, but only if we have Drag and Drop. An error results
  4450.     // in us pretending that we don't have drag support. Notice that in the test above, we also
  4451.     // require TextEdit to have TEGetHiliteRgn avalilable, which is always the case with the
  4452.     // present Drag Manager.
  4453.  
  4454.     if (gMachineInfo.haveDragMgr)
  4455.         {
  4456.         gGlobalTrackingHandler = NewDragTrackingHandlerProc(GlobalTrackingHandler);
  4457.         gGlobalReceiveHandler = NewDragReceiveHandlerProc(GlobalReceiveHandler);
  4458.         
  4459.         anErr = InstallTrackingHandler(gGlobalTrackingHandler, nil, nil);
  4460.  
  4461.         if (anErr == noErr)
  4462.             {
  4463.             anErr = InstallReceiveHandler(gGlobalReceiveHandler, nil, nil);
  4464.  
  4465.             if (anErr != noErr)
  4466.                 {
  4467.                 RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  4468.                 gMachineInfo.haveDragMgr = false;
  4469.                 }
  4470.             }
  4471.         else
  4472.             gMachineInfo.haveDragMgr = false;
  4473.         }
  4474.  
  4475.     return noErr;
  4476.     
  4477.     
  4478. // EXCEPTION HANDLING
  4479. BuildFontMenu:
  4480. GetNewMBar:
  4481. SysEnvirons:
  4482.     ConductErrorDialog(anErr, cNull, cancel);
  4483.     
  4484.     return anErr;
  4485.  
  4486. } // DoInitialize
  4487.  
  4488. // --------------------------------------------------------------------------------------------------------------
  4489. #pragma segment Terminate
  4490.  
  4491. static OSErr    DoTerminate(void)
  4492. {
  4493.     OSErr    anErr = noErr;
  4494.     
  4495.     if (gFontThread != kNoThreadID)
  4496.         DisposeThread(gFontThread, &gThreadResults, false);
  4497.     if (gAGThread != kNoThreadID)
  4498.         DisposeThread(gAGThread, &gThreadResults, false);
  4499.     if (gStarterThread != kNoThreadID)
  4500.         DisposeThread(gStarterThread, &gThreadResults, false);
  4501.  
  4502.     if (gMachineInfo.haveQuickTime)
  4503.         ExitMovies();
  4504.         
  4505.     if (gMachineInfo.haveTSMTE)
  4506.         CloseTSMAwareApplication();
  4507.  
  4508.     if (gMachineInfo.haveDragMgr)
  4509.         {
  4510.         RemoveReceiveHandler(gGlobalReceiveHandler, nil);
  4511.         RemoveTrackingHandler(gGlobalTrackingHandler, nil);
  4512.         }
  4513.  
  4514.     return anErr;
  4515.     
  4516. } // DoTerminate
  4517.  
  4518. // --------------------------------------------------------------------------------------------------------------
  4519. #pragma segment Main
  4520.  
  4521. main(void)
  4522. {
  4523.     OSErr    anErr;
  4524.     
  4525. #ifndef __MWERKS__
  4526.     UnloadSeg((Ptr) _DataInit);                        /* note that _DataInit must not be in Main! */
  4527. #endif
  4528.     MaxApplZone();                                    /* expand the heap so code segments load at the top */
  4529.     MoreMasters(); MoreMasters(); MoreMasters();     /* we love handles */
  4530.     anErr = DoInitialize();
  4531.     UnloadSeg((Ptr) DoInitialize);                    
  4532.     if (anErr == noErr)
  4533.         {
  4534.         DoEventLoop();
  4535.  
  4536. // REVIEW: don't want to unload the segment we're in!!
  4537. //        UnloadSeg((Ptr) DoEventLoop);
  4538.         DoTerminate();                    
  4539.         }
  4540.     return 0;
  4541. } // main
  4542.